Python para Ingeniería Comercial

Profesor: Félix Ordóñez

Mail: felix.ordonez@usach.cl

Horario: Martes bloque 2 y 3.

Programa

Objetivo

Este curso tiene por objetivo introducir un marco conceptual y práctico de programación en Python, junto con aplicaciones en economía y ciencia de datos. Este lenguaje fue desarrollado en 1989 en base a una programación multipropósito, de fácil legibilidad de código, gratuito y de código abierto. A partir de la última década, especialmente desde que se popularizó la librería pandas, el uso de este lenguaje incrementó ampliamente, donde se ha transversalizado en diferentes disciplinas como Data Science, Economía, Astronomía o Inteligencia Artificial, en contraposición de otros lenguajes enfocados sólo en cómputo numérico o estadístico.

Como Python es de código abierto, el desarrollo de nuevo contenido es constante y a gran velocidad, por ejemplo esto ha generado mecanismos para interactuar con otros lenguajes como R, SQL, C++ o Dynare++, se han creado librerías como Numba que permite mejorar sustancialmente el rendimiento de funciones y loops, o librerías dedicadas al Machine Learning como Keras, algo que sin duda ha impulsado su masificación en el mundo académico, el sector público y privado.

A lo largo del semestre se abordarán métodos básicos de programación, la utilización de librerías científicas, visualización y análisis de datos, junto con aplicaciones estadísticas y económicas. Al final del curso se espera que los estudiantes obtengan un nivel intermedio-avanzado en el manejo de Python.

Requerimientos

No es necesario tener conocimientos previos de programación.

Evaluaciones

  • Actividades cortas: 30%

  • Actividades intermedias: 30%

  • Trabajo en grupo: 40%

    • Informe 1: 10%

    • Informe 2: 10%

    • Presentación: 20%

Contenidos

  1. Introducción a Python:

  • Introducción a un lenguaje de propósito general.

  • Configuración de Anaconda y Jupyterlab.

  1. Utilizando Python

  • Aprendiendo lo básico: instalación de librerías, diferentes types y formatos, como utilizar listas, diccionarios y loops.

  • Funciones: son un bloque de código que puede (o no) recibir un input y mediante una sentencia reaiza una tarea y/o puede devolver un valor. Acá se aprenderá cómo se utilizan, argumentos de entrada y de salida, funciones anidadas, entre otros.

  • Clases y objetos: La programación orientada a objetos permite administrar el código mediante métodos y estructuras lógicas. En python se utilizan clases para estructurar el código creando un nuevo tipo de objeto. Analizaremos para qué sirven las clases y cómo se utilizan.

  • Utilización de datos con pandas: esta librería permite analizar bases de datos estructuradas. Se trabajará con bases relacionales como SQL, series de tiempo y matrices.

  • Introducción al manejo de texto: Una gran virtud de python es la facilidad con que se puede manejar archivos de texto. Para esto, se aprenderá cómo manejar bases de texto, depuración de datos, stopwords, y creación de word clouds.

  • Visualización de datos: manejo de Matplotlib y Plotly para crear diferentes tipos de gráficos, tablas y paneles dinámicos.

  • Optimización de código: uso de numba.

Aplicaciones

  • Oferta y demanda: solución de sistema de ecuaciones lineales.

  • OLS: realización de una regresión lineal para una ecuación de Mincer mediante datos de empleo chilenos.

  • Webscraping.

  • Modelo AR(1): programación de un modelo autoregresivo, gráficos de autocorrelación, persistencia del modelo.

  • Modelo de crecimiento de Solow: simulación del modelo de crecimiento, gráficos de resultados y análisis de diferentes paramterizaciones.

  • Colusión: programación de un modelo de cournot, funciones de demanda, costos y benficios, solución del equilibrio y gráficos.

  • Modelo de Markov-Switching: introducción al modelo de Hamilton (1989) y aplicación para el PIB de Chile.

  • Overlapping Generations Model (OLG): solución del estado estacionario de un modelo OLG, gráficos de capital y trabajo según generaciones.

  • Dynare++ y modelos DSGE: instalación de Dynare++ y utilización mediante jupyter, solución de modelos nuevo keynesianos.

Referencias

  • Aprende a instalar Anaconda en Windows, Mac o Linux. Acá puedes revisar la documentación de Conda en Windows, Mac o Linux.

  • Índice de paquetes e instalación.

  • Para introducirte en Jupyter.

  • Acá puedes encontrar un completo tutorial de Python.

  • Revisa las principales librerías que se usarán en el curso: NumPy, Pandas, Numba, Matplotlib, Plotly y SciPy.

  • Para una amplia programación en economía y finanzas usando Python está QuantEcon.

  • Proyectos de Python en GitHub.

  • Historia de Python y su desarrollo.

  • Stackoverflow tiene un gran foro de consultas y respuestas en materia de programación.

import pandas as pd
asdasd
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-4d01f3610a36> in <module>
      1 import pandas as pd
----> 2 asdasd

NameError: name 'asdasd' is not defined

Clase 2: About Python

¿Qué es Python?

Python es un lenguaje de propósito general creado en 1989 por Guido van Rossum.

La primera versión Python 0.9.0. fue lanzada en 1991, la versión Python 2.0 en el 2000. La versión 3.0 fue publicada en el 2008.

Python es gratuito y de código abierto. El desarrollo se encuentra coordinado por Python Software Fundation

Actualmente es uno de los lenguajes más populares.

Algunos ejemplos

Manejo de bases
import numpy as np
import pandas as pd
df = pd.read_csv("/home/felix/Dropbox/ayudantia1.csv", sep="\t")
df2= df.rename(columns = {'Tasa de desempleo': 'desempleo', 'PIB a precios corrientes': 'PIB' }, inplace = False)
df2[20:30]
Periodo desempleo PIB
20 01/03/15 6.23 39179.71
21 01/06/15 6.67 39535.45
22 01/09/15 6.54 38263.48
23 01/12/15 5.87 42574.71
24 01/03/16 6.49 41953.61
25 01/06/16 7.02 41447.91
26 01/09/16 7.03 40805.27
27 01/12/16 6.20 45330.60
28 01/03/17 7.04 43602.20
29 01/06/17 7.31 44040.88
Gráficos
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(19680801)

mu_x = 200
sigma_x = 25
x = np.random.normal(mu_x, sigma_x, size=100)

mu_w = 200
sigma_w = 10
w = np.random.normal(mu_w, sigma_w, size=100)

fig, axs = plt.subplots(nrows=2, ncols=2)

axs[0, 0].hist(x, 20, density=True, histtype='stepfilled', facecolor='g',
               alpha=0.75)
axs[0, 0].set_title('stepfilled')

axs[0, 1].hist(x, 20, density=True, histtype='step', facecolor='g',
               alpha=0.75)
axs[0, 1].set_title('step')

axs[1, 0].hist(x, density=True, histtype='barstacked', rwidth=0.8)
axs[1, 0].hist(w, density=True, histtype='barstacked', rwidth=0.8)
axs[1, 0].set_title('barstacked')

# Create a histogram by providing the bin edges (unequally spaced).
bins = [100, 150, 180, 195, 205, 220, 250, 300]
axs[1, 1].hist(x, bins, density=True, histtype='bar', rwidth=0.8)
axs[1, 1].set_title('bar, unequal bins')

fig.tight_layout()
plt.show()
_images/About_python_4_0.png
np.random.seed(19680801)
Z = np.random.rand(6, 10)
x = np.arange(-0.5, 10, 1)  # len = 11
y = np.arange(4.5, 11, 1)  # len = 7

fig, ax = plt.subplots()
ax.pcolormesh(x, y, Z)
<matplotlib.collections.QuadMesh at 0x7f0a2b6d0190>
_images/About_python_5_1.png
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np

fig, ax = plt.subplots(subplot_kw={"projection": "3d"})

# Make data.
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)

# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False)

# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
# A StrMethodFormatter is used automatically
ax.zaxis.set_major_formatter('{x:.02f}')

# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)

plt.show()
_images/About_python_6_0.png
Regresiones
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

import statsmodels.api as sm
from statsmodels.sandbox.regression.predstd import wls_prediction_std

np.random.seed(9876789)

nsample = 100
x = np.linspace(0, 10, 100)
X = np.column_stack((x, x**2))
beta = np.array([1, 0.1, 10])
e = np.random.normal(size=nsample)


X = sm.add_constant(X)
y = np.dot(X, beta) + e

model = sm.OLS(y, X)
results = model.fit()
print(results.summary())



nsample = 50
sig = 0.5
x = np.linspace(0, 20, nsample)
X = np.column_stack((x, np.sin(x), (x-5)**2, np.ones(nsample)))
beta = [0.5, 0.5, -0.02, 5.]

y_true = np.dot(X, beta)
y = y_true + sig * np.random.normal(size=nsample)
res = sm.OLS(y, X).fit()

prstd, iv_l, iv_u = wls_prediction_std(res)

fig, ax = plt.subplots(figsize=(8,6))

ax.plot(x, y, 'o', label="data")
ax.plot(x, y_true, 'b-', label="True")
ax.plot(x, res.fittedvalues, 'r--.', label="OLS")
ax.plot(x, iv_u, 'r--')
ax.plot(x, iv_l, 'r--')
ax.legend(loc='best');
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       1.000
Model:                            OLS   Adj. R-squared:                  1.000
Method:                 Least Squares   F-statistic:                 4.020e+06
Date:                Mon, 04 Apr 2022   Prob (F-statistic):          2.83e-239
Time:                        20:46:32   Log-Likelihood:                -146.51
No. Observations:                 100   AIC:                             299.0
Df Residuals:                      97   BIC:                             306.8
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          1.3423      0.313      4.292      0.000       0.722       1.963
x1            -0.0402      0.145     -0.278      0.781      -0.327       0.247
x2            10.0103      0.014    715.745      0.000       9.982      10.038
==============================================================================
Omnibus:                        2.042   Durbin-Watson:                   2.274
Prob(Omnibus):                  0.360   Jarque-Bera (JB):                1.875
Skew:                           0.234   Prob(JB):                        0.392
Kurtosis:                       2.519   Cond. No.                         144.
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
_images/About_python_8_1.png

Anaconda

Anaconda es “una distribución libre y abierta de los lenguajes Python y R”.

Tiene soporte para Windows, Mac o Linux.

_images/Anaconda1.png
_images/anaconda.png
_images/anaconda3.png

Ejemplo de cómo instalar https://www.youtube.com/watch?v=52h3r_lROGY&t=541s

Para Windows
_images/anaconda4.png
_images/anaconda5.png
_images/anaconda6.png
Para Mac

Una vez instaldo Anaconda, abrir una terminal. En la terminal, escribir python.

Si python está correctamente instalado les va a mostrar la versión junto a otra información. Por ejemplo

_images/anaconda7.png
_images/jupyter1.png

Tareas

  1. Chequear si Python está instalado.

  2. Usar Jupyter para abrir el archivo de intro.ipynb

  3. Instalar pandas usando conda.

  4. Instalar numpy usando pip.

  5. Responder el siguiente formulario: https://forms.gle/hBx1xD5fLBK5NF8QA

Clase 3-4: lo básico

  • Librerías

  • Funciones (intro)

  • Variables

  • Types

  • Listas

  • Arrays

  • Loops

Conceptos básicos clases anteriores:

  • Qué es python

  • Anaconda

  • Jupyter lab

  • Archivo notebook -> crear un nuevo notebook

  • Terminal

  • Ruta donde están nuestros archivos

  • Cómo instalar una librería usando conda o pip

1. Librerías

Antes de definir una librería necesitamos definir un módulo. Corresponde a un archivo con un código de Python.

Entonces, una librería va a ser una colección de módulos. Un paquete va a ser una librería que podemos instalar mediante un package manager.

¿Para qué utilizamos una librería?

Cada librería va a contener una colección de módulos, por ejemplo, funciones que nos van a permitir realizar operaciones matemáticas, graficar, importar datos, etc.

En esta clase vamos a utilizar la librería numpy (la instalamos la clase 2). Esta librería es fundamental para la computación científica, entregando un gran número de operaciones matemáticas.

Si no la tiene instalada aún, se puede hacer mediante el código pip install numpy. La documentación de numpy la encuentra en https://numpy.org/doc/stable/

Algunos ejemplos de qué podemos hacer con esta librería:

#1. Importar librería
import numpy as np
#2. usar librería para crear un vector
x = np.array([1,2,3])
#3. Realizar suma de valores dentro de un vector
np.sum(x)
#4. Valor absoluto
np.abs(x)
#5. Raíz cuadrada
np.sqrt(x)
#6. Transponer
np.transpose(x);

2. Funciones (intro)

Una función va a ser un código que almacena una tarea particular y puede ser llamada cuando uno quiera mediante el nombre de la función. Se le puede entregar un input y retorna un oputput.

En esta clase vamos a utilizar algunas funciones básicas como print() o type(). Las funciones se caracterizan por tener ‘nombre’ + ‘(input)’ o ‘nombre’ + ‘()’.

print('hola mundo')
x = 'hola mundo'
print(x)
print(x, type(x))
hola mundo
hola mundo
hola mundo <class 'str'>

3. Variables

Corresponden a un elemento que almacena información. Esta información puede tener diferentes typeso características. En lo que sigue veremos como crear una variable y los difentes tipos.

4. Types

Los lenguajes de programación tienen tipos de datos. Cada uno tiene diferentes propiedades y sirve para diferentes acciones. Python tiene los siguientes tipos de datos:

  • Texto: str

  • Número: int, float, comlex

  • Secuencial: list, tuple, range

  • Mapping: dict

  • Set: set, frozenset

  • Boolean: bool

  • Binario: bytes, bytearray, memoryview

Ejemplos
x = "Hola mundo"
print(x, type(x))
Hola mundo <class 'str'>
x = 20
print(x, type(x))
20 <class 'int'>
x = 20.5
print(x, type(x))
20.5 <class 'float'>
x = 1j
print(x, type(x))
1j <class 'complex'>
x = ["gatos", "perros", "canarios"]
print(x, type(x))
['gatos', 'perros', 'canarios'] <class 'list'>
x = ("gatos", "perros", "canarios")
print(x, type(x))
('gatos', 'perros', 'canarios') <class 'tuple'>
x = range(5)
print(x, type(x))
range(0, 5) <class 'range'>
x = {"gatos":2, "perros":3}
print(x, type(x))
{'gatos': 2, 'perros': 3} <class 'dict'>
x = True
print(x, type(x))
True <class 'bool'>
Ejercicios:
  1. Strings (texto)

  2. Int - Float

  3. Boolean

¿Cómo creamos una variable de texto?

#Para crear un string debe ser entre comillas
x = hola
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-12-48173a3c27f5> in <module>
      1 #Para crear un string debe ser entre comillas
----> 2 x = hola

NameError: name 'hola' is not defined
#Se pueden agregar
x = 'hola'
y = "mundo"
z = x + y
print(z)
holamundo
#Se pueden crear de diferentes maneras
x = "hola"
y = "mundo"
z = x + " " + y + "\n" + "un número puede ser string: " + "10 o " + str(10)
print(z)
hola mundo
un número puede ser string: 10 o 10
#Un número puede ser string -> no se pueden hacer operaciones matemáticas
x = "10"
y = "10"
z = x+y
print(z)
1010

Una variable del tipo numérica nos permite realizar operaciones matemáticas

x = 10 
y = 20.5
z = x + y
print(z)
30.5
x = (2 * 5 + 10)**2
print(x)
400

¿Se pueden juntar variables de diferentes tipos?

x = '10'
y = 10 
z = x + y
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-47-013104e6ffaf> in <module>
      1 x = '10'
      2 y = 10
----> 3 z = x + y

TypeError: can only concatenate str (not "int") to str
x = '10'
x = int(x)
y = 10 
z = x + y
print(z)
20

¿Cómo evalúo si se cumple una condición?

z == 10
False
x = True
y = False
print(x + y)
print(x + x)
print(y + y)
1
2
0

¿Se puede guardar una condición como variable?

x = 10
variable = x != 11
print("variable= ",variable)
variable=  True
x = 10
y = x != 11
print(y)
True

Para evaluar un “o” se utiliza “or”, mientras que para un “y” se usa “and”

print(x == 10 or y == False)
print(x == 10 and y == False)
print(x > 10 and y == True)
print(x <= 10 and y == True)
True
False
False
True

5. Listas

Una lista va a ser un contenedor de información. Permite diferentes types. Para crear una lista se pueden usar dos métodos:

  • Usando “[]”

  • o usando list()

x = ['a', 1, 1.1, True]
print(x)
['a', 1, 1.1, True]

Otra forma de crear listas:

x = list()
y = [x, x ] 
print(y)
[[], []]

¿Se puede acceder a los elementos de una lista?

x = ['a', 1, 1.1, True]
print(x[0])
print(x[1])
print(x[2])
a
1
1.1

Posición en una lista

La posición parte del índice 0.

Si la lista es [a, b, c], la posición de cada elemento será [0, 1, 2].

¿Se pueden hacer operaciones matemáticas con listas?

print(x + x)
['a', 1, 1.1, True, 'a', 1, 1.1, True]

y si son sólo elementos numéricos?

x = [1, 2]
y = [2, 2]
print(x + y)
print(x*x)
[1, 2, 2, 2]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-36-d32218a19255> in <module>
      2 y = [2, 2]
      3 print(x + y)
----> 4 print(x*x)

TypeError: can't multiply sequence by non-int of type 'list'

Si podemos realizar operaciones matemáticas sobre los elementos de una lista

print(x[0] + y[1])
3

¿Cómo agregar elementos a una lista?

x = list()
x.append(10)
x.append('hola')
print(x)
[10, 'hola']

Para acceder al total de elementos de una lista usamos la función len()

len(x)
2

Para borrar elementos podemos usar pop() o remove()

x = [10, 'hola']
x.pop(0)
print(x)
['hola']
x = [10, 'hola']
x.remove(10)
print(x)
['hola']

6. Arrays

Cuando queremos hacer operaciones matemáticas con vectores o matrices vamos a utilizar arrays.

Para esto usamos numpy: import numpy as np

import numpy as np
x = [1,2,3,4,5]
x_arr = np.array(x)
print("x:", x)
print("x_arr:", x_arr)
x: [1, 2, 3, 4, 5]
x_arr: [1 2 3 4 5]
print(type(x))
print(type(x_arr))
<class 'list'>
<class 'numpy.ndarray'>

La ventaja de usar arrays es que nos permite hacer operaciones matemáticas

x = np.array([0, 1, 2])
print(x + x)
print(x * 2)
print(x*x)
[0 2 4]
[0 2 4]
[0 1 4]
x = [[1, 2], [3, 4]]
x = np.array(x)
print(x)
print("len:", len(x), " x ", len(x[0]))
[[1 2]
 [3 4]]
len: 2  x  2

¿Si tenemos un vector y una matriz cómo realizamos una multiplicación?

\[\begin{split}\left[ \begin{array}{cc} 2 & 2 \\ 2 & 2 \end{array}\right] \cdot \left[ \begin{array}{cc} 2 & 2 \\ 2 & 2 \\ \end{array} \right] = \left[ \begin{array}{cc} 8 & 8 \\ 8 & 8 \\ \end{array} \right]\end{split}\]
x = np.array([[2, 2], [2, 2]])
#1. Multiplicación de cada elemento de la matriz
print("Caso 1:\n", x * x)
#2. Multiplicación de matrices
print("Caso 2:\n", x @ x)
Caso 1:
 [[4 4]
 [4 4]]
Caso 2:
 [[8 8]
 [8 8]]

7. Loops

Vamos a utilizar loops para crear iteraciones sobre una secuencia, un rango determinado, por ejemplo una lista, de 0 a 10, etc.

Para crear una iteración vamos a usar for

for i in range(0, 10): 
    print(i)
0
1
2
3
4
5
6
7
8
9
x = [0, 1, 'a', True]
for i in range(0, len(x)):
    print(i)
0
1
2
3
x = np.array([0, 1, 2, 3])
for i in range(0, len(x)):
    print(i)
0
1
2
3
x = [0, 1, 'a', True]
for i in x:
    print(i)
0
1
a
True
x = [1, 2, 3]
y = [4, 5 ,6]
for i in range(0, len(x)):
    print(x[i] + y[i])   
5
7
9
x = [1, 2, 3]
y = [4, 5 ,6]
for i in x:
    for j in y:
        print((i,j))
(1, 4)
(1, 5)
(1, 6)
(2, 4)
(2, 5)
(2, 6)
(3, 4)
(3, 5)
(3, 6)

Sintaxis va a ser la estructura del código.

#nombre de la variable, signo = , valor
x = 10 
#lista: nombre de la lista, signo = , [ valor ]
x = [10, 20]
#Iteración: for, iterador 'i', entre 'in', rango o lista (range(0, 10), [1,2,3]), ':'
for i in range(0, 10): 
    print(i)
    
#Una vez me salgo del espacio se acaba la iteración
x = 10
0
1
2
3
4
5
6
7
8
9
x = 10
z = 'b'
a = list()
print(a)
a.append(x)
print(a)
a = [x, z]
print(a)
a.append('b')
print(a)
[]
[10]
[10, 'b']
[10, 'b', 'b']

Actividad en clases

  1. ¿Cuál es la diferencia entre una librería y una función? de un ejemplo de cada una.

  2. ¿Cuál es la diferencia entre un type y una variable? muestre con un ejemplo de código.

  3. ¿Cuál es la diferencia entre una lista y un array?

  4. ¿Para qué nos sirve un loop?

  5. Crear 5 variables que se llamen “var_” + type: str, float, int, bool, list. Estas variables deben ser del type respectivo.

  6. ¿De qué type son las siguientes operaciones?

  • 10*10.5

  • True*False

  • True*10

  • str(10) + str(20.5)

  • int(str(10)) + float(str(20.5))

  • int(10.5)

  • [1, 10., “a”]

  1. Crear un array de 2x2 que tenga los elementos (1,2,3,4) y mostrar cómo se puede acceder al elemento 4.

  2. evalúe si [1,2,3] es igual a np.array([1,2,3]). Entregue una intuición.

  3. Utilice for y print() para mostrar mediante un loop números de 0 al 10 y luego otro loop para mostrar los elementos del 5 al 10.

  4. El siguiente código tiene un error. El objetivo es sumar los iteradores en un loop. Explique el problema que tiene el código y por qué no llega al resultado (45).

for i in range(10): 
  x = 0
  x = x+i
x
9

Clase 5-6: lo básico (continuación)

En esta clase veremos:

  • Loops

  • Input/output

  • If

Conceptos claves que revisamos la clase anterior, deberíamos responder qué son, para qué se ocupan, cómo se ocupan:

  • Librerías

  • Funciones (intro)

  • Variables

  • Types

  • Listas

  • Arrays

1. Loops

Vamos a utilizar loops para crear iteraciones sobre una secuencia, un rango determinado, por ejemplo una lista, de 0 a 10, etc.

Para crear una iteración vamos a usar for.

La sintaxis es: for + iterador + in + sobre lo que iteramos + :

#Lo más básico es que iteramos sobre un rango
#un rango es un tipo de datos entre ciertos valores
print(range(5))


#Podemos iterar sobre un rango
for i in range(0, 10): 
    print(i)
range(0, 5)
0
1
2
3
4
5
6
7
8
9
#Podemos iterar sobre una lista: iteramos en el rango de valores que tiene la lista
x = [0, 1, 'a', True]
for i in range(0, len(x)):
    print(i)
0
1
2
3
#Podemos iterar sobre un array
import numpy as np
x = np.array([0, 1, 2, 3])
for i in range(0, len(x)):
    print(i)
0
1
2
3
#Podemos iterar sobre los elementos de la lista
x = [0, 1, 'a', True]
for i in x:
    print(i)
0
1
a
True
#Podemos usar la iteración para acceder a elementos de una lista

x = [1, 2, 3]
y = [4, 5 ,6]
for i in range(0, len(x)):
    print(x[i] + y[i])   
5
7
9
#Podemos hacer iteraciones anidadas (for dentro de un for)
x = [1, 2, 3]
y = [4, 5 ,6]
for i in x:
    for j in y:
        print((i,j))
(1, 4)
(1, 5)
(1, 6)
(2, 4)
(2, 5)
(2, 6)
(3, 4)
(3, 5)
(3, 6)

Un aspecto clave es entender dónde existe una variable o lista, y si está dentro o fuera de un loop qué es lo que pasa.

#nombre de la variable, signo = , valor
x = 10 
#lista: nombre de la lista, signo = , [ valor ]
x = [10, 20]
#Iteración: for, iterador 'i', entre 'in', rango o lista (range(0, 10), [1,2,3]), ':'
for i in range(0, 10): 
    print(i)
    
#Una vez me salgo del espacio se acaba la iteración
x = 10
0
1
2
3
4
5
6
7
8
9

Si creamos una variable antes de la iteración podemos modificarla con cada iteración. Veamos tres ejemplos con x, y, z:

# Creamos x y y fuera de la iteración
x = 0 
y = 0
for i in range(1, 5): 
    z = 0 #creamos z dentro de la iteración
    x = x + i
    y = i 
    z = z + i
    print("i:", i, ", x=", x, ", y=", y, ", z=", z)
i: 1 , x= 1 , y= 1 , z= 1
i: 2 , x= 3 , y= 2 , z= 2
i: 3 , x= 6 , y= 3 , z= 3
i: 4 , x= 10 , y= 4 , z= 4

Podemos utilizar iteraciones dentro de iteraciones, esto nos puede servir para trabajar con diferentes fuentes de información.

import numpy as np

#linspace(inicio, término, cantidad de elementos)
a = np.linspace(1, 10, 5)
print("type de a:", type(a))
print("a: ", a)
#arange(inicio, término, separador)
b = np.arange(1, 10,2)
print("type de b:", type(b))
print("b: ", b)
type de a: <class 'numpy.ndarray'>
a:  [ 1.    3.25  5.5   7.75 10.  ]
type de b: <class 'numpy.ndarray'>
b:  [1 3 5 7 9]

Vamos a utilizar estas listas para crear un for anidado y guardar un resultado en una lista

#1. Creamos una lista
lista1 = []
#2. Iteramos sobre los valoes de a y b
for i in a: 
    for j in b: 
        #3. Anexamos una operación a la lista
#         print((i, j), "multiplicacion:", i * j)
        lista1.append(i * j) 
print(lista1)
[1.0, 3.0, 5.0, 7.0, 9.0, 3.25, 9.75, 16.25, 22.75, 29.25, 5.5, 16.5, 27.5, 38.5, 49.5, 7.75, 23.25, 38.75, 54.25, 69.75, 10.0, 30.0, 50.0, 70.0, 90.0]
#1. Creamos una lista
lista2 = []
#2. Iteramos sobre el largo de las listas a y b
for i in range(0, len(a)): 
    for j in range(0, len(b)): 
        #3. Anexamos una operación a la lista
        lista2.append(a[i] * b[j]) 
print(lista2)

#Podemos sumar los valores de una lista con sum()
print("suma de la lista:", sum(lista2))
[1.0, 3.0, 5.0, 7.0, 9.0, 3.25, 9.75, 16.25, 22.75, 29.25, 5.5, 16.5, 27.5, 38.5, 49.5, 7.75, 23.25, 38.75, 54.25, 69.75, 10.0, 30.0, 50.0, 70.0, 90.0]
suma de la lista: 687.5

Son iguales la lista 1 y lista 2?

lista1 == lista2
True

2. Input/Output

La función input() nos va a pedir entregar un valor. Esto es útil cuando necesitamos que el usuario nos reporte algún dato.

input()
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
<ipython-input-13-9c8b639daf2f> in <module>
----> 1 input()

~/miniconda3/lib/python3.9/site-packages/ipykernel/kernelbase.py in raw_input(self, prompt)
    843         """
    844         if not self._allow_stdin:
--> 845             raise StdinNotImplementedError(
    846                 "raw_input was called, but this frontend does not support input requests."
    847             )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

Podemos incorporar un texto referente a la información solicitada

input("Indique su edad:")

¿Podemos guardar la información?

edad = input("indique su edad:") 
#Es una variable del tipo string
#¿Cómo la pasamos a una variable numérica?
#Con int() o float()
print(edad)

3. If/else

La operación if nos permite evaluar si se cumple una condición.

Por ejemplo: Iteramos sobre una lista entre (0, 10), si el valor es mayor que 5 muestra el resultado.

  • Iteramos sobre una lista entre (0, 10): hacemos un for entre 0 y 10.

  • si el valor es mayor que 5: condición

  • muestra el resultado: operación

for i in range(0, 10): #iteración
    if i > 5: #condición
        print(i) #operación
6
7
8
9

¿Qué tipo de operaciones podemos evaluar? Operaciones lógicas:

  • Igualdad: ==

  • Desigualdad: !=

  • Mayor: >

  • Mayor que: >=

  • Menor: <

  • Menor que: <=

a = 10 
if a==10: 
    x = a
print(x)
10

Para evaluar varias situaciones vamos a usar if, elif y else.

Ejemplo: Vamos a crear una lista A utilizando valores de una segunda lista B con valores entre 0-10. Vamos a iterar sobre los valores de la lista A y realizar las siguientes operaciones

  • Si el valor está entre 0-3 guardamos ese mismo valor en B

  • Si el valor es mayor que 3 y menor que 5 lo multiplicamos por 2

  • Si el valor es igual a 5 pedir ingresar un valor con la función input()

  • Para todo el resto eleva el número al cuadrado

#1. Creamos las listas
A = np.arange(0, 11, 1)
B = []

#2. Iteramos sobre A
for i in A: 
    #Creamos la primera condición
    if i>=0 and i<= 3: 
        B.append(i)
    #Segunda condición
    elif i > 3 and i<5: 
        B.append(i*2)
    #Tercera condición    
    elif i==5:
        B.append(float(input("Agregar un valor")))
        B.append(input("input 2:"))
    #Para todo el resto
    else: 
        B.append(i**2)
Agregar un valor 10
input 2: 10
print("A:", A)
print("B:", B)
A: [ 0  1  2  3  4  5  6  7  8  9 10]
B: [0, 1, 2, 3, 8, 10.0, '10', 36, 49, 64, 81, 100]
x = input("agrega un valor")
type(x)
agrega un valor 10
str
a = 'texto2'
if a != 'texto':
    print("ok")
ok
a = [0, 'x', 3]
for i in range(0, len(a)): 
    print(a[i])
    if i == 0:
        print("ok")
    elif i == 1:
        print("ok2")
     
0
ok
x
ok2
3

Actividad

Instrucciones:

  • Cada pregunta la debe responder en una celda diferente (en total 15 celdas de código).

  • Antes de cada celda de código debe ir un markdown que muestre la pregunta que está respondiendo.

  • En la celda de código debe agregar un breve comentario de qué está haciendo.

Acá un ejemplo de cómo va a responder:

  1. Importe una librería

#Importar librería
import numpy as np

Responda las siguientes preguntas.

  1. Crear un libro de jupyter en su carpeta para actividades del curso. Nombre el libro como Actividad_1

  2. Crear una variable de texto, una numérica y otra boolean.

  3. Explique brevemente qué ocurre con la variable x si ejecuto el siguiente código:

    • x = 10 ,

    • luego x = x + 10

  4. ¿Es lo mismo range(5) y range(0,5)? muestre su resultado mediante una variable boolean.

  5. Crear una lista vacía llamda ‘L1’, luego anexar las tres variables del punto 2. a ‘L1’.

  6. Borrar la variable numérica de L1.

  7. Realice un for entre el rango 0-10 y realice los siguientes dos ejemplos:

    • Antes del for cree una variable x = 0, dentro del for sume 1 (x = x + 1)

    • Dentro del for cree una variable x = 0, luego sume 1 (x = x + 1)

    • ¿Cuál es la diferencia?

  8. Genere un array A de dimensiones 2x2 y otro B de dimensiones 2x1. Realice un loop anidado (for dentro de for) para multiplicar cada elemento de A por el de B. Guárdelos en una lista.

Clase 7-8

Repaso conceptos clave:

  • Librería: Colección de modulos. Nos sirve para implementar funciones que permiten tareas específicas como: operaciones matemáticas, gráficos, procesar datos, etc.

  • Variables: elemento que amacena información de diferentes types.

  • Types: Tipos de información que podemos utilizar: texto, numérico, secuencial, boolean, etc.

  • Listas: Contenedor de información. Permite almacenar diferentes types.

  • Loops: iteraciones que vamos a llamar mediante la palabra for.

Contenidos de la clase:

  • Loops

  • Input/output

  • If

  • Crear funciones

  • Diccionarios

1. Loops

Podemos iterar sobre un rango de valores (a,b). Recordar que la iteración incluye a y no incluye b.

# Iterar sobre un rango de valores
for i in range(5, 10): 
    print(i)
5
6
7
8
9

Si creamos una variable antes de la iteración podemos modificarla con cada iteración. Veamos tres ejemplos con x, y, z:

# Creamos x y y fuera de la iteración
x = 0 
y = 0
for i in range(1, 5): 
    z = 0 #creamos z dentro de la iteración
    x = x + i
    y = i 
    z = z + i
    print("i:", i, ", x=", x, ", y=", y, ", z=", z)
i: 1 , x= 1 , y= 1 , z= 1
i: 2 , x= 3 , y= 2 , z= 2
i: 3 , x= 6 , y= 3 , z= 3
i: 4 , x= 10 , y= 4 , z= 4

Podemos utilizar iteraciones dentro de iteraciones, esto nos puede servir para trabajar con diferentes fuentes de información.

La clase pasada vimos como crear una lista manualmente, ahora dos ejemplos para crear una lista numérica (array) entre un rango de valores:

import numpy as np
#linspace(inicio, término, cantidad de elementos)
a = np.linspace(1, 10, 5)
print("type de a:", type(a))
print("a: ", a)
#arange(inicio, término, separador)
b = np.arange(1, 10,2)
print("type de b:", type(b))
print("b: ", b)
type de a: <class 'numpy.ndarray'>
a:  [ 1.    3.25  5.5   7.75 10.  ]
type de b: <class 'numpy.ndarray'>
b:  [1 3 5 7 9]
import numpy as np
inicio = 0
termino = 100
cantidad = 1001

np.linspace(inicio, termino, cantidad)

(termino-inicio)/cantidad
0.0999000999000999

Vamos a utilizar estas listas para crear un for anidado y guardar un resultado en una lista

Recorrer los elementos de la lista

for i in b: 
    print(i)
    
    
1
3
5
7
9
print(a)
print(b)
[ 1.    3.25  5.5   7.75 10.  ]
[1 3 5 7 9]
#1. Creamos una lista
lista1 = []
#2. Iteramos sobre los valoes de a y b
for i in a: 
    for j in b: 
        #3. Anexamos una operación a la lista
#         print((i, j), "multiplicacion:", i * j)
        lista1.append(i * j) 
print(lista1)
[1.0, 3.0, 5.0, 7.0, 9.0, 3.25, 9.75, 16.25, 22.75, 29.25, 5.5, 16.5, 27.5, 38.5, 49.5, 7.75, 23.25, 38.75, 54.25, 69.75, 10.0, 30.0, 50.0, 70.0, 90.0]

Otra forma de hacer la misma operación

#1. Creamos una lista
lista2 = []
#2. Iteramos sobre el largo de las listas a y b
for i in range(0, len(a)): 
    for j in range(0, len(b)): 
        #3. Anexamos una operación a la lista
        lista2.append(a[i] * b[j]) 
print(lista2)

#Podemos sumar los valores de una lista con sum()
print("suma de la lista:", sum(lista2))
[1.0, 3.0, 5.0, 7.0, 9.0, 3.25, 9.75, 16.25, 22.75, 29.25, 5.5, 16.5, 27.5, 38.5, 49.5, 7.75, 23.25, 38.75, 54.25, 69.75, 10.0, 30.0, 50.0, 70.0, 90.0]
suma de la lista: 687.5

Son iguales la lista 1 y lista 2?

lista1 == lista2
True

2. Input/Output

La función input() nos va a pedir entregar un valor. Esto es útil cuando necesitamos que el usuario nos reporte algún dato.

input()
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
<ipython-input-10-9c8b639daf2f> in <module>
----> 1 input()

~/miniconda3/lib/python3.9/site-packages/ipykernel/kernelbase.py in raw_input(self, prompt)
    843         """
    844         if not self._allow_stdin:
--> 845             raise StdinNotImplementedError(
    846                 "raw_input was called, but this frontend does not support input requests."
    847             )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.

Podemos incorporar un texto referente a la información solicitada

input("Indique su edad:")
Indique su edad: 10
'10'

¿Podemos guardar la información?

edad = input("indique su edad:") 
#Es una variable del tipo string
#¿Cómo la pasamos a una variable numérica?
#Con int() o float()
indique su edad: 10
print(edad)
10

3. If/else

La operación if nos permite evaluar si se cumple una condición.

Por ejemplo: Iteramos sobre una lista entre (0, 10), si el valor es mayor que 5 muestra el resultado.

  • Iteramos sobre una lista entre (0, 10): hacemos un for entre 0 y 10.

  • si el valor es mayor que 5: condición

  • muestra el resultado: operación

for i in range(0, 10): #iteración
    if i > 5: #condición
        print(i) #operación
6
7
8
9

¿Qué tipo de operaciones podemos evaluar? Operaciones lógicas:

  • Igualdad: ==

  • Desigualdad: !=

  • Mayor: >

  • Mayor que: >=

  • Menor: <

  • Menor que: <=

a = 10 
if a==10: 
    x = a
print(x)
10

Operaciones conjuntas con “or” y “and”

if a == 10 and x >= 5: 
    print((a, x))
(10, 10)

Para evaluar varias situaciones vamos a usar if, elif y else.

Ejemplo: Vamos a crear una lista A utilizando valores de una segunda lista B con valores entre 0-10. Vamos a iterar sobre los valores de la lista A y realizar las siguientes operaciones

  • Si el valor está entre 0-3 guardamos ese mismo valor en B

  • Si el valor es mayor que 3 y menor que 5 lo multiplicamos por 2

  • Si el valor es igual a 5 pedir ingresar un valor con la función input()

  • Para todo el resto eleva el número al cuadrado

#1. Creamos las listas
A = np.arange(0, 11, 1)
B = []

#2. Iteramos sobre A
for i in A: 
    #Creamos la primera condición
    if i>=0 and i<= 3: 
        B.append(i)
    #Segunda condición
    elif i > 3 and i<5: 
        B.append(i*2)
    #Tercera condición    
    elif i==5:
        B.append(float(input("Agregar un valor")))
        B.append(input("input 2:"))
    #Para todo el resto
    else: 
        B.append(i**2)
Agregar un valor 10
input 2: 10
print("A:", A)
print("B:", B)
A: [ 0  1  2  3  4  5  6  7  8  9 10]
B: [0, 1, 2, 3, 8, 10.0, '10', 36, 49, 64, 81, 100]
np.zeros(0)
array([], dtype=float64)
x = input("agrega un valor")
type(x)
agrega un valor 1
str
a = 'texto2'
if a != 'texto':
    print("ok")
ok
a = [0, 'x', 3]
for i in range(0, len(a)): 
    print(a[i])
    if i == 0:
        print("ok")
    elif i == 1:
        print("ok2")
        
0
ok
x
ok2
3

4. Crear funciones

Una función es un código que nos permite almacenar una una acción. A la función le damos un nombre y luego la podemos invocar utilizando ese nombre.

Un ejemplo de función es print(), mediante su nombre podemos pedir que muestre una variable, un texto u otro.

Ahora vamos a ver cómo se crea una función.

Sintaxis: “def” + “nombre” + “(input)” + “:”

def miprimerafuncion(a): #declaramos la función y establecemos el input
    b = a*2  #definimos qué hace la función
    return b #Establecemos un output

#definimos el input
a = 10
#llamamos la función
miprimerafuncion(a)
20

podemos evaluar la función con diferentes valores

b = 100
print(miprimerafuncion(b))
print(miprimerafuncion(1000))
200
2000

A una función le podemos dar diferentes input y que entregue variados output

def ejemplo2(a, b): 
    return a*b, a**b
ejemplo2(10,2)
(20, 100)

podemos guardar los valores del output de la función

X, Y = ejemplo2(10,2)
print("X=", X, "Y=", Y)
X= 20 Y= 100

El input puede ser de diferentes types

def ejemplo3(a):
    AA = []
    AA.append(a)
    return AA
C = ejemplo3("hola")
print(C)
['hola']
print(C)
['hola']

Variable local y global

Una variable local va a existir en un lugar específico. Por ejemplo dentro de la función ejemplo3(a) definimos A=[], esta variable A existe sólo dentro de la función ejemplo3(), es decir es una variable local.

Por otro lado, cuando decimos que A = ejemplo3(“hola”) estamos definiendo una vable global, que existe en todo el espacio del Jupyter, una vez definida puede ser llamada en en cualquier parte.

El ejemplo que utilizamos para probar if/ifelse/else lo podemos guardar en una función que le entremos una lista numérica (A) y que retorne una lista B con las operaciones del if

#1. Creamos la función con un input A
def ejemplo4(A):
    #2. Creamos una lista  vacía
    B = []

    #3. Iteramos sobre A
    for i in A: 
        #Creamos la primera condición
        if i>=0 and i<= 3: 
            B.append(i)
        #Segunda condición
        elif i > 3 and i<5: 
            B.append(i*2)
        #Tercera condición
        elif i==5:
            B.append(input("Agregar un valor"))
        #Todo el resto
        else: 
            B.append(i**2)

    #4. Output
    return B
#Creamos una lista entre 0-10
A = np.arange(0, 11, 1)
#Llamamos la función y guardamos el resultado en una variable global
B = ejemplo4(A)
Agregar un valor 1
print("A:", A)
print("B:", B)
A: [ 0  1  2  3  4  5  6  7  8  9 10]
B: [0, 1, 2, 3, 8, '1', 36, 49, 64, 81, 100]

5. Diccionarios

Un diccionario es una estructura que nos permite almacenar información mediante una llave (key) y un valor. Vamos a llamar tupla a esta llave:valor.

Por ejemplo si tenemos una base como

\[\begin{split}\begin{array}{ccc} \text{Nombre} & \text{Edad} & \text{Signo zodiacal} \\ \text{Ana} & 30 & \text{Libra} \\ \text{Félix} & 50 & \text{Sagitario} \end{array} \end{split}\]

Podemos crear dos diccionarios, uno para Ana y otro para Félix del tipo:

  • Ana: {Nombre: Ana, Edad:30, Signo:Libra}

  • Félix: {Nombre:Félix, Edad:50, Signo:Sagitario}

En este caso las llaves son (Nombre, Edad y Signo).

Para crear un diccionario utilizamos “{}”. La sintaxis es “{” + “llave” + “:” + “valor” + “}”

Ana = {'nombre':'ana', 'edad':30, 'signo':'libra'}
Felix = {'nombre':'felix', 'edad':50, 'signo':'sagitario'}

Para acceder a los elementos del diccionario vamos a usar la llave o el valor

print('La edad de Ana es '+ str(Ana['edad']) + ' y su signo es ' + Ana['signo'])
La edad de Ana es 30 y su signo es libra

Vamos a usar las funciones keys(), values() e items() para ver los valores dentro de un diccionario

#Para ver las llaves
Ana.keys()
dict_keys(['nombre', 'edad', 'signo'])
#Para ver los valores
Felix.values()
dict_values(['felix', 50, 'sagitario'])
#Para ver las tuplas (llave, valor)
Felix.items()
dict_items([('nombre', 'felix'), ('edad', 50), ('signo', 'sagitario')])

Clase 9-10: Funciones, diccionarios y condicionales

#Importar librería
import numpy as np

En esta classe vamos a ejercitar respondiendo las siguientes preguntas

  1. Utilice las funciones de numpy linspace() y arange() para crear dos vectores entre 0-100. El primer vector tiene 1000 elementos entre el rango 0-100 y el segundo se encuentra separado por 1.5 (ejm: 0, 1.5, 3, …).

#Crear vector entre 0-100 con 1000 elementos
vector1 = np.linspace(0, 100, 1000)
print("len(vector1):", len(vector1))

#Crear vector entre 0-100 separados por 1.5
vector2 = np.arange(0, 100, 1.5)
print("len(vector2):", len(vector2))

#Vectores
print("vector1:", vector1[0:10])
print("vector2:", vector2[0:10])
len(vector1): 1000
len(vector2): 67
vector1: [0.        0.1001001 0.2002002 0.3003003 0.4004004 0.5005005 0.6006006
 0.7007007 0.8008008 0.9009009]
vector2: [ 0.   1.5  3.   4.5  6.   7.5  9.  10.5 12.  13.5]
  1. Utilizando los vectores de 1. crear un loop anidado para calcular la multiplicación entre cada elemento de los vectores. Guarde los resultados en una lista.

#Lista vacía
lista = []

#Loop
for i in vector1: 
    for j in vector2: 
        lista.append(i*j)
        
print("len(lista):", len(lista))
print("len(vector1)*len(vector2):", len(vector1)*len(vector2))
len(lista): 67000
len(vector1)*len(vector2): 67000
  1. Mediante un print() muestre la suma, el promedio, la mediana y la desviación estándar de la lista guardada en 2.

#Suma
print("suma:", np.sum(lista))
#Promedio
print("promedio:", np.mean(lista))
#Mediana
print("mediana:", np.median(lista))
#Desviación estandar
print("Std:", np.std(lista))
suma: 165825000.0
promedio: 2475.0
mediana: 1839.3393393393394
Std: 2202.8088300489767
  1. Utilice la función input() para solicitar la cantidad de mascotas.

#Solicitar input
input("¿Cuántas mascotas tiene?")
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
<ipython-input-5-1e0ab58c6baf> in <module>
      1 #Solicitar input
----> 2 input("¿Cuántas mascotas tiene?")

~/miniconda3/lib/python3.9/site-packages/ipykernel/kernelbase.py in raw_input(self, prompt)
    843         """
    844         if not self._allow_stdin:
--> 845             raise StdinNotImplementedError(
    846                 "raw_input was called, but this frontend does not support input requests."
    847             )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
  1. Crear una rutina que permita las siguientes operaciones:

    1. Solicitar el número de mascotas (con input()), guardar en una variable.

    2. Si el número de mascotas es cero: imprimir “No tiene mascotas.”

    3. Si el número es entre 1-3: imprimir “Tiene entre 1-3 mascotas.”

    4. Si el número es entre 4-6: imprimir “Tiene entre 4-6 mascotas.”

    5. Si el número es mayor que 6: imprimir “Tiene más de 6 mascotas”.

    6. Si el número es negativo: imprimir “No corresponde”.

    *Probar que su rutina cumple bien todas las condiciones. *

mascotas = int(input("¿Cuántas mascotas tiene?"))
if mascotas == 0: 
    print("No tiene mascotas")
elif mascotas >0 and mascotas <=3 : 
    print("Tiene entre 1-3 mascotas")
elif mascotas>3 and mascotas <=6:
    print("Tiene entre 4-6 mascotas")
elif mascotas>6: 
    print("Tiene mas de 6 mascotas")
elif mascotas<0: 
    print("No corresponde")
else: 
    print("otro caso")
    
¿Cuántas mascotas tiene? 0
No tiene mascotas
  1. Crear dos listas, una con estilo musical (blues, rock, rap, trap, pop) y otra con grupos (Nina Simone, Led Zeppelin, Makiza, Pablo Chill-e, Dua Lipa). Luego crear una función que tenga como input “estilo” y output “recomendacion”. La función debe permitir:

    1. Leer el input estilo y compararlo con la lista de estilo musical.

    2. Utilizar if/elif/else para crear condiciones que recomiende música. Por ejemplo, si estilo es igual a Blues, recomendar Nina Simone.

    3. Si el estilo no está en la lista de estilo musical, debe mostrar “No tenemos recomendación para su estilo musical”.

    4. El output debe ser la recomendación.

    Evaluar si la función se ejecuta correctamente para cada estilo musical

Opción 1

def recomendacion(estilo): 
    if estilo == "blues": 
        print("Nina Simone")
    elif estilo == "rock": 
        print("Led Zeppelin")
    elif estilo == "rap": 
        print("Makiza")
    elif estilo == "trap": 
        print("Pablo Chill-e")
    elif estilo == "pop":
        print("Dua Lipa")
    else: 
        print("No tenemos recomendación para su estilo musical.")
        
recomendacion('rap')
Makiza

Opción 2

def recomendacion(estilo): 
    if estilo == "blues": 
        recomend = "Nina Simone"
    elif estilo == "rock": 
        recomend = "Led Zeppelin"
    elif estilo == "rap": 
        recomend = "Makiza"
    elif estilo == "trap": 
        recomend = "Pablo Chill-e"
    elif estilo == "pop":
        recomend = "Dua Lipa"
    else: 
        recomend = "No tenemos recomendación para su estilo musical."
    return recomend
        
#Podemos guardar la recomendación en una variable
recomendacion('blues')
'Nina Simone'

Opción 3

estilos = ["blues", "rock", "rap", "trap", "pop"]
grupos = ["Nina Simone", "Led Zeppelin", "Makiza", "Pablo Chill-e", "Dua Lipa"]

def recomendacion(estilo): 
    for i in range(0, len(estilos)): 
        if estilo == estilos[i]:
            recomend = grupos[i]
            break
        else: 
            recomend = "No tenemos recomendación"
    return recomend

recomendacion("rock")
'Led Zeppelin'
  1. Crear una función que permita evaluar hasta tres estilos musicales y entregue una lista con las recomendaciones. Por ejemplo, si le entrego una lista con [rock, rap, pop] me debe retornar [Led Zeppelin, Makiza, Dua Lipa]. Adicionalmente:

    • Si le entrego una lista con 1 o 2 estilos musicales debe entregar 1 o 2 recomendaciones respectivamente.

    • Si le entrego una lista del tipo [trap, musica clasica] debe retornar la recomendación para trap [Pablo Chill-e].

    • Si le entrego una lista del tipo [musica clasica] debe retornar un mensaje “no tenemos recomendación para su estilo musical”.

Pasos de la iteración

  1. “blues”: igual a la pregunta 6

  2. [“blues”]:

    • si el lago del input == 1

    • iteramos sobre el total de estilos

    • si input (estilo) en la posición cero (primer elemento) es igual al valor de los estilos (sobre lo que estamos iterando)

    • entonces guardar el output como el grupo en la posición “i”.

    • cierre

  3. [“blues”, “rock”]:

    • si el largo del input es igual a 2

    • creamos lista vacía para guardar las recomendaciones

    • iteramos sobre el total de elementos de estilos

    • Comparamos el primer elemento del input con los estilos ejm: si input[“blues”, rock”], comparamos “blues” con cada elemento de estilos.

    • Si son iguales, guardamos la recomendación (grupos[i]) en la lista, en caso contrario no hacemos nada.

    • Repetimos para el segundo elemento del input (“rock”).

  4. [“rock”, “blues”, “pop”]

    • La lógica es la misma que en 3., sólo que ahora agregamos un tercer elemento y con ello una tercera condición.

estilos = ["blues", "rock", "rap", "trap", "pop"]
grupos = ["Nina Simone", "Led Zeppelin", "Makiza", "Pablo Chill-e", "Dua Lipa"]

def recomendacion(estilo): 
    #Entrada como texto: ejm "rock"
    if type(estilo) == str: 
        for i in range(0, len(estilos)): 
            if estilo == estilos[i]:
                recomend = grupos[i]
                break
            else: 
                recomend = "No tenemos recomendación" 
                
    #Entrada como lista ej ["rock"] o ["rock", "blues"]
    else:
        #1 estilo musical
        if len(estilo) == 1:
            for i in range(0, len(estilos)): 
                if estilo[0] == estilos[i]:
                    recomend = grupos[i]
                    break
                else: 
                    recomend = "No tenemos recomendación"           

        #2 estilos musicales
        elif len(estilo) == 2:
            recomend = []
            for i in range(0, len(estilos)): 
                if estilo[0] == estilos[i]:
                    recomend.append(grupos[i])
                elif estilo[1] == estilos[i]:
                    recomend.append(grupos[i])
        #3 estilos musicales: 
        elif len(estilo) == 3:
            recomend = []
            for i in range(0, len(estilos)): 
                if estilo[0] == estilos[i]:
                    recomend.append(grupos[i])
                elif estilo[1] == estilos[i]:
                    recomend.append(grupos[i])    
                elif estilo[2] == estilos[i]:
                    recomend.append(grupos[i])          
        
    return recomend
gustos = ["rap", "rock", "blues"]
recomendacion(gustos)
['Nina Simone', 'Led Zeppelin', 'Makiza']
  1. Crear una diccionario a partir de las dos listas donde la llave sea el estilo musical y el valor sea el grupo, ej: {‘rap’:’makiza’…}.

musica_dic = {'blues':'Nina Simone', 'rock':'Led Zeppelin', 'rap':'Makiza', 'trap':'Pablo Chill-e', 'pop':'Dua Lipa'  }
print("diccionario:", musica_dic)
print("llaves:", musica_dic.keys())
print("valores:", musica_dic.values())
print("items:", musica_dic.items())
diccionario: {'blues': 'Nina Simone', 'rock': 'Led Zeppelin', 'rap': 'Makiza', 'trap': 'Pablo Chill-e', 'pop': 'Dua Lipa'}
llaves: dict_keys(['blues', 'rock', 'rap', 'trap', 'pop'])
valores: dict_values(['Nina Simone', 'Led Zeppelin', 'Makiza', 'Pablo Chill-e', 'Dua Lipa'])
items: dict_items([('blues', 'Nina Simone'), ('rock', 'Led Zeppelin'), ('rap', 'Makiza'), ('trap', 'Pablo Chill-e'), ('pop', 'Dua Lipa')])
  1. Replicar la función 6. pero ahora utilizando diccionarios donde la función haga lo siguiente:

    1. Evaluar si el input (estilo) está en las llaves del diccionario.

    2. Si el input está en llave del diccionario recomendar el grupo musical respectivo.

    3. Si no está en la llave del diccionario retornar “No tenemos recomendación para su estilo musical”.

Opción 1

musica_dic = {'blues':'Nina Simone', 'rock':'Led Zeppelin', 'rap':'Makiza', 'trap':'Pablo Chill-e', 'pop':'Dua Lipa'  }

def recomendacion(estilo): 
    for k in musica_dic.keys(): 
        if estilo == k:
            recomend = musica_dic[k]
            break
        else: 
            recomend = "No tenemos recomendación"
    return recomend

recomendacion("pop")
'Dua Lipa'

Opción 2

def recomendacion(estilo): 
    for k,v in musica_dic.items(): 
        if estilo == k:
            recomend = v
            break
        else: 
            recomend = "No tenemos recomendación"
    return recomend

recomendacion("rap")
'Makiza'
  1. Replicar la función de 7. utilizando diccionarios.

musica_dic = {'blues':'Nina Simone', 'rock':'Led Zeppelin', 'rap':'Makiza', 'trap':'Pablo Chill-e', 'pop':'Dua Lipa'  }
def recomendacion(estilo): 
    #Entrada como texto: ejm "rock"
    if type(estilo) == str: 
        for k, v in musica_dic.items(): 
            if estilo == k:
                recomend = v
                break
            else: 
                recomend = "No tenemos recomendación" 
                
    #Entrada como lista ej ["rock"] o ["rock", "blues"]
    else:
        #1 estilo musical
        recomend = []
        if len(estilo) == 1:
            for k, v in musica_dic.items(): 
                if estilo[0] == k:
                    recomend = v
                    break
                else: 
                    recomend = "No tenemos recomendación"     

        #2 estilos musicales
        elif len(estilo) == 2:
            recomend = []
            for k, v in musica_dic.items(): 
                if estilo[0] == k:
                    recomend.append(v)
                elif estilo[1] == k:
                    recomend.append(v)
        #3 estilos musicales: 
        elif len(estilo) == 3:
            recomend = []
            for k, v in musica_dic.items(): 
                if estilo[0] == k:
                    recomend.append(v)
                elif estilo[1] == k:
                    recomend.append(v)    
                elif estilo[2] == k:
                    recomend.append(v)          
        
    return recomend
gustos = ["AA"]
recomendacion(gustos)
'No tenemos recomendación'
  1. Crear una función que entregue una edad como un valor aleatorio entre 15-65 años y educación como otro valor aleatorio entre 0-22, pero tiene que ser menor que la edad.

import random
import numpy as np
#Caso 1: una sola persona
def fun_edad_educ(eda_in, eda_end, edu_in, edu_end):
    edad = random.randint(eda_in, eda_end)
    educ = random.randint(edu_in, edu_end)
    if edad<educ:
        educ = random.randint(edu_in, edu_end)
            
    return edad, educ
fun_edad_educ(15, 65, 0, 22) #->que pasa si el if no es suficiente
(43, 7)
#Caso 1: una sola persona
def fun_edad_educ(eda_in, eda_end, edu_in, edu_end):
    edad = random.randint(eda_in, eda_end)
    educ = random.randint(edu_in, edu_end)
    while edad<educ:
        edad = random.randint(eda_in, eda_end)
        educ = random.randint(edu_in, edu_end)
            
    return edad, educ
fun_edad_educ(15, 65, 50, 100)
(62, 57)
#Caso 3: varias persona
def fun_edad_educ(n, eda_in, eda_end, edu_in, edu_end):
    edad_list = []
    educ_list = []
    for i in range(n):
        edad = random.randint(eda_in, eda_end)
        educ = random.randint(edu_in, edu_end)
        if edad<educ:
            educ = random.randint(edu_in, edu_end)
        edad_list.append(edad)
        educ_list.append(educ)
    return np.array(edad_list), np.array(educ_list)
edad_list, educ_list = fun_edad_educ(20, 15, 65, 0, 22)
#Caso 4: varias persona
def fun_edad_educ(n, eda_in, eda_end, edu_in, edu_end):
    edad_list = []
    educ_list = []    
    for i in range(n):
        edad = random.randint(eda_in, eda_end)
        educ = random.randint(edu_in, edu_end)
        while edad<educ:
            edad = random.randint(eda_in, eda_end)
            educ = random.randint(edu_in, edu_end)
            
        edad_list.append(edad)
        educ_list.append(educ)            
            
    return np.array(edad_list), np.array(educ_list)
edad_list, educ_list = fun_edad_educ(20, 15, 65, 50, 100)
(array([56, 58, 63, 58, 58, 54, 64, 57, 62, 61, 65, 64, 59, 62, 55, 54, 65,
        65, 65, 54]),
 array([51, 52, 60, 50, 52, 54, 64, 52, 53, 56, 60, 52, 55, 53, 50, 52, 54,
        50, 58, 53]))
  1. Crear una función que calcule la experiencia como edad-educación

def fun_exp(edad, educ):
    return edad-educ
exp = fun_exp(edad_list, educ_list)
  1. Crear una función que calcule la estimación del ingreso según la ecuación de Mincer: \(Ln(Y) = \beta_0 + \beta_1 S + \beta_2 Exp + \beta_3 Exp^2\) Donde:

  • \(S\): años de educación

  • \(Exp\): experiencia

  • \(Exp^2\): experiencia al cuadrado

Utilice: \(\beta_0=9.7\), \(\beta_1=0.14\), \(\beta_2=0.07\), \(\beta_3=-0.001\)

def mincer(S, Exp):
    β0 = 9.7
    β1 = 0.14
    β2 = 0.07
    β3 = -0.001
    return β0 + β1*S + β2*Exp + β3*Exp**2

ln_y = mincer(educ_list, exp)
import matplotlib.pyplot as plt
plt.plot(educ_list, ln_y, 'o')
[<matplotlib.lines.Line2D at 0x7f8d284574c0>]
_images/Clase9_10_43_1.png

Actividad 1

Instrucciones:

  • Cada pregunta la debe responder en una celda diferente (en total 4 celdas de código).

  • Antes de cada celda de código debe ir un markdown que muestre la pregunta que está respondiendo.

  • En la celda de código debe agregar un breve comentario de qué está haciendo.

  • Mandar el archivo PDF al correo felix.ordonez@usach.cl con el asunto “Actividad 1”.

  • Fecha de entrega: antes de la clase del 09/11.

  • En caso que no respete las instrucciones se le descontará puntaje de la nota

Nombre:

  1. Crear una función que entregue una edad como un valor aleatorio entre 15-65 años, educación como otro valor aleatorio entre 0-22 (tiene que ser menor que la edad) y sexo igual a una variable dicotómica (0 o 1) aleatoria.

  2. Crear una función que calcule la experiencia como edad-educación

  3. Crear una función que calcule la estimación del ingreso según la ecuación de Mincer: \(Ln(y)=\beta_0+\beta_1S+\beta_2Exp+\beta_3Exp^2 + \beta_4 Sexo + \beta_5(Sexo*S)\)

Donde:

  • \(S\): años de educación

  • \(Exp\): experiencia

  • \(Exp^2\): experiencia al cuadrado

Utilice: \(\beta_0=9.7\), \(\beta_1=0.15\), \(\beta_2=0.08\), \(\beta_3=-0.0011\), \(\beta_4=-0.15\), \(\beta_5=-0.0136\)

  1. Relice el proceso en una iteración para N = 100 personas, guarde los resultados mediante un diccionario que la llave sea la persona (\(n=1,\cdots,N\)) y el valor sea una lista del tipo: [S, Exp, Exp2, Sexo, Sexo*S, Ln(y)]. Muestre los resultados de su diccionario.

Clase 11-12: Introducción a los gráficos

En esta clase vamos introducir una librería para realizar visualizaciones. Para estas visualizaciones vamos a poder utilizar diferentes typescomo números, textos, listas o diccionarios.

Hoy vamos a revisar la librería matplotlib que abreviamos como plt.

Ejemplo de visualizaciones (no marearse con el código)

import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)


N = 50
x = np.random.rand(N)
y = np.random.rand(N)
colors = np.random.rand(N)
area = (30 * np.random.rand(N))**2  # 0 to 15 point radii

plt.scatter(x, y, s=area, c=colors, alpha=0.5)
plt.show()
_images/Clase11_12_2_0.png
import numpy as np
import matplotlib.pyplot as plt


data = [[ 66386, 174296,  75131, 577908,  32015],
        [ 58230, 381139,  78045,  99308, 160454],
        [ 89135,  80552, 152558, 497981, 603535],
        [ 78415,  81858, 150656, 193263,  69638],
        [139361, 331509, 343164, 781380,  52269]]

columns = ('Freeze', 'Wind', 'Flood', 'Quake', 'Hail')
rows = ['%d year' % x for x in (100, 50, 20, 10, 5)]

values = np.arange(0, 2500, 500)
value_increment = 1000

# Get some pastel shades for the colors
colors = plt.cm.BuPu(np.linspace(0, 0.5, len(rows)))
n_rows = len(data)

index = np.arange(len(columns)) + 0.3
bar_width = 0.4

# Initialize the vertical-offset for the stacked bar chart.
y_offset = np.zeros(len(columns))

# Plot bars and create text labels for the table
cell_text = []
for row in range(n_rows):
    plt.bar(index, data[row], bar_width, bottom=y_offset, color=colors[row])
    y_offset = y_offset + data[row]
    cell_text.append(['%1.1f' % (x / 1000.0) for x in y_offset])
# Reverse colors and text labels to display the last value at the top.
colors = colors[::-1]
cell_text.reverse()

# Add a table at the bottom of the axes
the_table = plt.table(cellText=cell_text,
                      rowLabels=rows,
                      rowColours=colors,
                      colLabels=columns,
                      loc='bottom')

# Adjust layout to make room for the table:
plt.subplots_adjust(left=0.2, bottom=0.2)

plt.ylabel("Loss in ${0}'s".format(value_increment))
plt.yticks(values * value_increment, ['%d' % val for val in values])
plt.xticks([])
plt.title('Loss by Disaster')

plt.show()
_images/Clase11_12_3_0.png
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(19680801)

# example data
mu = 100  # mean of distribution
sigma = 15  # standard deviation of distribution
x = mu + sigma * np.random.randn(437)

num_bins = 50

fig, ax = plt.subplots()

# the histogram of the data
n, bins, patches = ax.hist(x, num_bins, density=True)

# add a 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
     np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('Smarts')
ax.set_ylabel('Probability density')
ax.set_title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')

# Tweak spacing to prevent clipping of ylabel
fig.tight_layout()
plt.show()
_images/Clase11_12_4_0.png
import matplotlib.pyplot as plt
import numpy as np

np.random.seed(19680801)
data = np.random.randn(2, 100)

fig, axs = plt.subplots(2, 2, figsize=(5, 5))
axs[0, 0].hist(data[0])
axs[1, 0].scatter(data[0], data[1])
axs[0, 1].plot(data[0], data[1])
axs[1, 1].hist2d(data[0], data[1])

plt.show()
_images/Clase11_12_5_0.png

Introducción a los gráficos

import matplotlib.pyplot as plt

Para hacer un gráfico vamos a usar la función plot y show para mostrar (es como el print() de los gráficos).

Utilizando la lista [1,2,3,4] podemos hacer una línea recta

¿Es de 45°?

y = [1,2,3,4]
plt.plot(y)
plt.show()
_images/Clase11_12_10_0.png

Lo anterior es equivalente a graficar

y = [1,2,3,4]
x = [0, 1, 2, 3]
plt.plot(x, y)
plt.show()
_images/Clase11_12_12_0.png

Entonces si queremos hacer una línea de 45°

plt.plot(x, x)
plt.show()
_images/Clase11_12_14_0.png

Los gŕaficos funcionan como capas, si colocamos un código seguido del otro (sin el plt.show()) se agregan al mismo plot.

y = [1,2,3,4]
plt.plot(y) #No es de 45°
plt.plot(y, y) #Es de 45°
plt.show()
_images/Clase11_12_16_0.png

Entonces si queremos graficar (x, y) donde x = [1, 2, …, 10], \(y = 2x^2+1\).

import numpy as np #Importamos la librería numérica
x = np.linspace(1, 10, 10) #Creamos un vector numérico entre 1-10 con 10 elementos
y = 2*x**2 +1  #Creamos la ecuación

plt.plot(x, y) #Graficamos
plt.show()
_images/Clase11_12_18_0.png

Otro gráfico y = log(x)

x = np.linspace(1, 100, 1000) #Creamos un vector entre 1-100 con 1000 elementos
y = np.log(x)  #Sacamos el logaritmo de cada valor de x

plt.plot(x, y) #Graficamos (x, y)
plt.show()
plt.show()
_images/Clase11_12_20_0.png

¿Cómo agregamos títlo y nombres de ejes?

x = np.linspace(1, 100, 1000) #Valores de x
y = np.log(x)  #Valores de y

plt.plot(x, y)
plt.title("Gráfico Logaritmo") #título
plt.xlabel("x") #título eje x
plt.ylabel("y") #título eje y
plt.show()
_images/Clase11_12_22_0.png

Para agregar una leyenda usamos plt.legend()

plt.plot(x, y)
plt.title("Gráfico Logaritmo") #título
plt.xlabel("x") #título eje x
plt.ylabel("y") #título eje y
plt.legend("A") #Leyenda
plt.show()
_images/Clase11_12_24_0.png

Entonces podemos graficar varias situaciones a partir de las diferentes capas.

#Ponderadores
α1 = 0.1
α2 = 0.4
α3 = 1
α4 = 1.5

#Valores de x e y
x = np.linspace(1, 100, 1000)
y1 = np.log(x)*α1
y2 = np.log(x)*α2
y3 = np.log(x)*α3
y4 = np.log(x)*α4


plt.plot(x, y1, label="α1="+str(α1))
plt.plot(x, y2, label="α2="+str(α2))
plt.plot(x, y3, label="α3="+str(α3))
plt.plot(x, y4, label="α4="+str(α4))
plt.title("Gráfico Logaritmo") #título
plt.xlabel("x") #título eje x
plt.ylabel("y") #título eje y
plt.legend()
plt.show()
_images/Clase11_12_26_0.png

Gráfico de barra: recordar que los gráficos funcionan como capas

import matplotlib.pyplot as plt

# Datos
Valor = [1, 12, 7, 20, 60]
barras = ('A', 'B', 'C', 'D', 'E')
y_pos = np.arange(len(barras))

# Creamos las barras
plt.bar(y_pos, Valor)

# Creamos los nombres en el eje
plt.xticks(y_pos, barras)

# Mostrar el gráfico
plt.show()
_images/Clase11_12_28_0.png

Le podemos cambiar la intensidad del color

# Datos
Valor = [1, 12, 7, 20, 60]
barras = ('A', 'B', 'C', 'D', 'E')
y_pos = np.arange(len(barras))

# Creamos las barras
plt.bar(y_pos, Valor, alpha=0.5) #Con el parámetro alpha cambiamos la intensidad del color

# Creamos los nombres en el eje
plt.xticks(y_pos, barras)

# Mostrar el gráfico
plt.show()
_images/Clase11_12_30_0.png

Podemos cambiar el color usando el argumento “color”.

Para ver las opciones de colores pueden revisar el siguiente link 3: https://matplotlib.org/stable/gallery/color/named_colors.html

# Datos
Valor = [1, 12, 7, 20, 60]
barras = ('A', 'B', 'C', 'D', 'E')
y_pos = np.arange(len(barras))

# Creamos las barras
plt.bar(y_pos, Valor, color='red', alpha=0.8) #con "color" modificamos el color

# Creamos los nombres en el eje
plt.xticks(y_pos, barras)

# Mostrar el gráfico
plt.show()
_images/Clase11_12_32_0.png

Para modificar la orientación del gráfico usamos “plt.barh” en lugar de “plt.bar” y los nombres de eje ahora se ponen en “plt.yticks”.

# Datos
Valor = [1, 12, 7, 20, 60]
barras = ('A', 'B', 'C', 'D', 'E')
y_pos = np.arange(len(barras))

# Creamos las barras
plt.barh(y_pos, Valor, color='red', alpha=0.8) #con "color" modificamos el color

# Creamos los nombres en el eje
plt.yticks(y_pos, barras)

# Mostrar el gráfico
plt.show()
_images/Clase11_12_34_0.png

Con estos ejemplos básicos, junto a un poco de información adicional podemos crear un gráfico de barras para dos grupos diferentes.

Para esto vamos a utilizar el argumento “width” que nos va a permitir separar las barras.

Para ver los otros argumentos de los “plot.bar” pueden ver el siguiente link 4: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.bar.html

Valor1 = [1, 12, 7, 20, 60]
Valor2 = [10, 5, 12, 20, 40]

barras = ('A', 'B', 'C', 'D', 'E')
y_pos = np.arange(len(barras))
bar_width = 0.35
# Creamos las barras

'''barra 1''' #Podemos escribir los elementos del gráfico separados por coma y por saltos de línea
plt.bar(y_pos, Valor1, 
        bar_width, 
        color='red', 
        alpha=0.8, 
        label = "Caso 1") 

'''barra 2'''
plt.bar(y_pos+bar_width, Valor2, 
        bar_width, 
        color='green', 
        alpha=0.6, 
        label = "Caso 2") 

# Creamos los nombres en el eje
plt.xticks(y_pos+bar_width/2, barras)
plt.legend() #Para que nos muestre los labels
# Mostrar el gráfico
plt.show()
_images/Clase11_12_36_0.png

Algunas preguntas para seguir utilizando gráficos:

  • ¿Podemos colocar un gráfico dentro de una función?

  • ¿Nos sirven los condicionales para un gráfico?

  • ¿Qué otros tipos de gráficos podemos crear?

  • ¿Nos sirven los loops para simplificar los códigos de un gráfico?

Actividad en clases

Usando el ejemplo de Mincer de la clase anterior, vamos a graficar los resultados.

Tenemos las funciones:

  • fun_edad_educ()

  • fun_exp()

  • mincer()

import random
import numpy as np
def fun_edad_educ(n, eda_in, eda_end, edu_in, edu_end):
    edad_list = []
    educ_list = []    
    for i in range(n):
        edad = random.randint(eda_in, eda_end)
        educ = random.randint(edu_in, edu_end)
        while edad<educ:
            edad = random.randint(eda_in, eda_end)
            educ = random.randint(edu_in, edu_end)
            
        edad_list.append(edad)
        educ_list.append(educ)            
            
    return np.array(edad_list), np.array(educ_list)

def fun_exp(edad, educ):
    return edad-educ


def mincer(S, Exp):
    β0 = 9.7
    β1 = 0.14
    β2 = 0.07
    β3 = -0.001
    return β0 + β1*S + β2*Exp + β3*Exp**2
#Obtener: edad y educación
edad, educ = fun_edad_educ(100, 15, 65, 0, 22)
#Obtener: experiencia
exp = fun_exp(edad, educ)
#Obtener: ln(y)
ln_y = mincer(educ, exp)
  1. Crear un gráfico de puntos (scatter) entre edad y educación.

  2. Crear un gráfico de puntos entre experiencia (eje horizontal) y ln_y (eje vertical), donde los puntos sean rojos, agregar título de ejes, título de gráfico, y una línea punteada de 45°.

  3. Crear un gráfico de barras con el promedio, mediana, y desviación estándar de ln_y.

  4. Crear un histograma usando el ln_y.

Clase 13: introducción a los datos

En esta clase revisaremos como importar datos, diferentes formatos que podemos utilizar y algunos problemas típicos a la hora del manejo de información.

La librería que vamos a ocupar para el manejo de datos es pandas.

Conceptos clave:

  • Pandas

  • DataFrame

  • Delimitador de miles y decimales

  • Tipo de variable

  • Index

1. Introducción

import pandas as pd

Para importar una base de datos en formato csvvamos a utilizar pd.read_csv(ruta/archivo)

En el caso de Windows llamamos la ruta con doble “\(\backslash \backslash\)”, por ejemplo: “C:\(\backslash \backslash\)Users\(\backslash \backslash\)…”

Vamos a utilizar una base csv del banco central con información de empleo. Lo primero que llama la atención es que la base importa mal debido al delimitador, la base viene con “;” y por default viene “,”.

pd.read_csv("/home/felix/Dropbox/Computational_Economics/Intro_python/2021_S2/Clases/clase13_base1.csv")
Periodo;1.Total;2.Empleadores;3.Cuenta Propia;4.Asalariados;5.Personal de servicio;6.Familiar no remunerado
mar.2010;7.156 21;318 32;1.289 68;5.141 76;325 38;81 8
abr.2010;7.198 78;324 94;1.332 33;5.114 80;331 31;95 39
may.2010;7.181 90;326 95;1.346 54;5.080 65;328 56;99 21
jun.2010;7.221 58;328 03;1.384 28;5.074 00;327 60;107 68
jul.2010;7.256 52;333 82;1.390 03;5.081 93;339 44;111 29
... ... ... ... ... ... ...
nov.2020;7.916 72;248 89;1.568 05;5.833 68;188 43;77 66
dic.2020;8.026 22;234 57;1.588 14;5.927 28;194 91;81 32
ene.2021;8.121 42;237 25;1.610 63;6.000 74;197 43;75 36
feb.2021;8.167 62;245 25;1.634 08;6.018 35;198 73;71 20
mar.2021;8.148 21;246 92;1.646 38;5.978 29;204 48;72 14

133 rows × 1 columns

Para cambiar el delimitador vamos a usar delimiter

pd.read_csv("/home/felix/Dropbox/Computational_Economics/Intro_python/2021_S2/Clases/clase13_base1.csv", delimiter=";")
Periodo 1.Total 2.Empleadores 3.Cuenta Propia 4.Asalariados 5.Personal de servicio 6.Familiar no remunerado
0 mar.2010 7.156,21 318,32 1.289,68 5.141,76 325,38 81,08
1 abr.2010 7.198,78 324,94 1.332,33 5.114,80 331,31 95,39
2 may.2010 7.181,90 326,95 1.346,54 5.080,65 328,56 99,21
3 jun.2010 7.221,58 328,03 1.384,28 5.074,00 327,60 107,68
4 jul.2010 7.256,52 333,82 1.390,03 5.081,93 339,44 111,29
... ... ... ... ... ... ... ...
128 nov.2020 7.916,72 248,89 1.568,05 5.833,68 188,43 77,66
129 dic.2020 8.026,22 234,57 1.588,14 5.927,28 194,91 81,32
130 ene.2021 8.121,42 237,25 1.610,63 6.000,74 197,43 75,36
131 feb.2021 8.167,62 245,25 1.634,08 6.018,35 198,73 71,20
132 mar.2021 8.148,21 246,92 1.646,38 5.978,29 204,48 72,14

133 rows × 7 columns

Si el archivo se encuentra en la misma carpeta que el Jupyter se puede llamar sólo con el nombre del csv.

pd.read_csv("clase13_base1.csv", delimiter=";")
Periodo 1.Total 2.Empleadores 3.Cuenta Propia 4.Asalariados 5.Personal de servicio 6.Familiar no remunerado
0 mar.2010 7.156,21 318,32 1.289,68 5.141,76 325,38 81,08
1 abr.2010 7.198,78 324,94 1.332,33 5.114,80 331,31 95,39
2 may.2010 7.181,90 326,95 1.346,54 5.080,65 328,56 99,21
3 jun.2010 7.221,58 328,03 1.384,28 5.074,00 327,60 107,68
4 jul.2010 7.256,52 333,82 1.390,03 5.081,93 339,44 111,29
... ... ... ... ... ... ... ...
128 nov.2020 7.916,72 248,89 1.568,05 5.833,68 188,43 77,66
129 dic.2020 8.026,22 234,57 1.588,14 5.927,28 194,91 81,32
130 ene.2021 8.121,42 237,25 1.610,63 6.000,74 197,43 75,36
131 feb.2021 8.167,62 245,25 1.634,08 6.018,35 198,73 71,20
132 mar.2021 8.148,21 246,92 1.646,38 5.978,29 204,48 72,14

133 rows × 7 columns

El resultado es una estructura del tipo fila-columna que podemos guardar en una variable que llamaremos DataFrame.

Un DataFrame corresponde a una estructura de datos del tipo fila-columna (similar a una hoja de excel) en el que podemos guardar información de diferentes types. Los DataFrame tienen un índice en la primera columna que parte en 0.

Nuestro DataFrame tiene 133 filas y 7 columnas, donde el índice va de 0 a 132.

df = pd.read_csv("clase13_base1.csv", delimiter=";")

2. Accediendo a un DataFrame

La primera mirada a nustros datos se la vamos a dar con la función head(), esta nos muestra un resumen de la tabla. Para esto ponemos “nombre del DataFrame”+ “.” + “head()”.

Esto nos va a mostrar las columnas de la base y las primeras 5 filas.

df.head()
Periodo 1.Total 2.Empleadores 3.Cuenta Propia 4.Asalariados 5.Personal de servicio 6.Familiar no remunerado
0 mar.2010 7.156,21 318,32 1.289,68 5.141,76 325,38 81,08
1 abr.2010 7.198,78 324,94 1.332,33 5.114,80 331,31 95,39
2 may.2010 7.181,90 326,95 1.346,54 5.080,65 328,56 99,21
3 jun.2010 7.221,58 328,03 1.384,28 5.074,00 327,60 107,68
4 jul.2010 7.256,52 333,82 1.390,03 5.081,93 339,44 111,29

Podemos decir específicamente cuántas filas queremos ver colocando el número dentro del paréntesis de head(10).

df.head(10)
Periodo 1.Total 2.Empleadores 3.Cuenta Propia 4.Asalariados 5.Personal de servicio 6.Familiar no remunerado
0 mar.2010 7.156,21 318,32 1.289,68 5.141,76 325,38 81,08
1 abr.2010 7.198,78 324,94 1.332,33 5.114,80 331,31 95,39
2 may.2010 7.181,90 326,95 1.346,54 5.080,65 328,56 99,21
3 jun.2010 7.221,58 328,03 1.384,28 5.074,00 327,60 107,68
4 jul.2010 7.256,52 333,82 1.390,03 5.081,93 339,44 111,29
5 ago.2010 7.289,22 333,77 1.430,97 5.080,20 339,49 104,79
6 sep.2010 7.389,47 339,06 1.477,55 5.130,21 338,02 104,63
7 oct.2010 7.414,43 343,68 1.485,61 5.150,80 331,05 103,29
8 nov.2010 7.503,09 347,12 1.486,07 5.216,86 341,96 111,08
9 dic.2010 7.572,32 340,32 1.486,32 5.294,66 342,20 108,82

La función dtypes nos va a describir la información dentro de la base de datos.

df.dtypes
Periodo                     object
1.Total                     object
2.Empleadores               object
3.Cuenta Propia             object
4.Asalariados               object
5.Personal de servicio      object
6.Familiar no remunerado    object
dtype: object

El type object corresponde a un dato del tipo texto, como una palabra. En este caso es poco intuitivo frente al tipo de datos que estamos usando. Deberíamos esperar que la base fuese en su mayoría del tipo numérico (Float, Int).

Para esto podemos especificar dos cosas:

  • Decimal: usamos el argumento decimal="separador".

  • Separador de miles: usamos el argumento thousands="separador"

Esto es relevante porque según la configuración del computador e idioma las bases pueden venir con separadores “.” o “,”. En nuestro caso la base viene con separador de decimal “,” y con separador de miles “.”.

#1. Guarda el DataFrame, muestra las columnas y la cantidad de filas y columnas
#IMportar datos
df = pd.read_csv("clase13_base1.csv", delimiter=";", decimal=",", thousands='.')
#Muestra los tipos
print(df.dtypes)
#Muestra columnas
print(df.columns)
#Mostrar N fila- M columna
print(df.shape)
Periodo                      object
1.Total                     float64
2.Empleadores               float64
3.Cuenta Propia             float64
4.Asalariados               float64
5.Personal de servicio      float64
6.Familiar no remunerado    float64
dtype: object
Index(['Periodo', '1.Total', '2.Empleadores', '3.Cuenta Propia',
       '4.Asalariados', '5.Personal de servicio', '6.Familiar no remunerado'],
      dtype='object')
(133, 7)

Para ver las columnas del DataFrame usamos columns

df.columns
Index(['Periodo', '1.Total', '2.Empleadores', '3.Cuenta Propia',
       '4.Asalariados', '5.Personal de servicio', '6.Familiar no remunerado'],
      dtype='object')

Las dimensiones fila-columna las podemos ver mediante shape. Esta viene en formato tupla (fila,columna)

df.shape
(133, 7)

Para ver el final de la tabla podemos usar tail()

df.tail()
Periodo 1.Total 2.Empleadores 3.Cuenta Propia 4.Asalariados 5.Personal de servicio 6.Familiar no remunerado
128 nov.2020 7916.72 248.89 1568.05 5833.68 188.43 77.66
129 dic.2020 8026.22 234.57 1588.14 5927.28 194.91 81.32
130 ene.2021 8121.42 237.25 1610.63 6000.74 197.43 75.36
131 feb.2021 8167.62 245.25 1634.08 6018.35 198.73 71.20
132 mar.2021 8148.21 246.92 1646.38 5978.29 204.48 72.14

Para revisar una columna en específico podemos usar diferentes mecanismos

#Caso 1
df['Periodo']
#Caso 2
df.Periodo
0      mar.2010
1      abr.2010
2      may.2010
3      jun.2010
4      jul.2010
         ...   
128    nov.2020
129    dic.2020
130    ene.2021
131    feb.2021
132    mar.2021
Name: Periodo, Length: 133, dtype: object

¿Qué pasa cuando el nombre de nuestra columna viene con espacios? ¿Podemos usar el caso 2?

#Funciona
df['3.Cuenta Propia']
#No Funciona
df.'3.Cuenta Propia'
  File "<ipython-input-15-3e2cc8594c50>", line 4
    df.'3.Cuenta Propia'
       ^
SyntaxError: invalid syntax

Por esta razón es fundamental que los nombres sean simples, en caso que tengan más de una palabra separar con “_”.

3. Manipulando el DataFrame

Lo primero que haremos es modificar el nombre de las variables. Para esto podemos usar el la función rename() y un diccionario con {‘nombre_antiguo’:’nombre_nuevo’}.

df.rename(columns={'1.Total': 'TOT', '2.Empleadores':'EMP', '3.Cuenta Propia':'CP', '4.Asalariados':'ASA', '5.Personal de servicio':'PdS', '6.Familiar no remunerado':'FnR'})
Periodo TOT EMP CP ASA PdS FnR
0 mar.2010 7156.21 318.32 1289.68 5141.76 325.38 81.08
1 abr.2010 7198.78 324.94 1332.33 5114.80 331.31 95.39
2 may.2010 7181.90 326.95 1346.54 5080.65 328.56 99.21
3 jun.2010 7221.58 328.03 1384.28 5074.00 327.60 107.68
4 jul.2010 7256.52 333.82 1390.03 5081.93 339.44 111.29
... ... ... ... ... ... ... ...
128 nov.2020 7916.72 248.89 1568.05 5833.68 188.43 77.66
129 dic.2020 8026.22 234.57 1588.14 5927.28 194.91 81.32
130 ene.2021 8121.42 237.25 1610.63 6000.74 197.43 75.36
131 feb.2021 8167.62 245.25 1634.08 6018.35 198.73 71.20
132 mar.2021 8148.21 246.92 1646.38 5978.29 204.48 72.14

133 rows × 7 columns

Si hacemos sólo df.rename() no se modifica el DataFrame, entonces tenemos dos opciones: 1) creamos uno nuevo o 2) modificamos el que ya existe.

#1. Creamos lun DF nuevo 
df2 = df.rename(columns={'1.Total': 'TOT', '2.Empleadores':'EMP', '3.Cuenta Propia':'CP', '4.Asalariados':'ASA', '5.Personal de servicio':'PdS', '6.Familiar no remunerado':'FnR'})

#2. Modificamos el que existe
df = df.rename(columns={'1.Total': 'TOT', '2.Empleadores':'EMP', '3.Cuenta Propia':'CP', '4.Asalariados':'ASA', '5.Personal de servicio':'PdS', '6.Familiar no remunerado':'FnR'})
df.head()
Periodo TOT EMP CP ASA PdS FnR
0 mar.2010 7156.21 318.32 1289.68 5141.76 325.38 81.08
1 abr.2010 7198.78 324.94 1332.33 5114.80 331.31 95.39
2 may.2010 7181.90 326.95 1346.54 5080.65 328.56 99.21
3 jun.2010 7221.58 328.03 1384.28 5074.00 327.60 107.68
4 jul.2010 7256.52 333.82 1390.03 5081.93 339.44 111.29

Una mirada inicial a una variable podemos darla con la función describe(). Esta función nos entrega una resumen estadístico de la variable.

#1. Llamamos una variable df.variable
df.TOT.describe()

#2. Llamamos df['variable']
df['TOT'].describe()
count     133.000000
mean     8187.411504
std       504.204366
min      7073.190000
25%      7844.780000
50%      8202.890000
75%      8535.210000
max      9118.180000
Name: TOT, dtype: float64

Podemos sacar una estadística en particular

print("min:", df.TOT.min())
print("max:", df.TOT.max())
print("mean:", df.TOT.mean())
print("std:", df.TOT.std())
print("count:", df.TOT.count())
min: 7073.19
max: 9118.18
mean: 8187.411503759398
std: 504.20436565714255
count: 133

La variable Periodo sigue siendo del tipo Objeto (texto), podemos crear una variable del tipo fecha. Para esto vamos a hacer dos cosas: 1) crear una variable en formato fecha y 2) agregar esta variable al DataFrame.

  • Para crear una variable del tipo fecha podemos usar la función date_range(fecha_inicio, periodos, frecuencia). En el siguiente link (link 3) encuentran detalle de como variables del tipo fecha en un DataFrame: https://pandas.pydata.org/pandas-docs/stable/user_guide/timeseries.html.

  • Para anexar una variable al DataFrame colocamos df['nombre_variable'] = variable.

#Creamos una variable del tipo fecha con la función
Date = pd.date_range("2010-03-01", periods=133, freq="M")

#Anexamos la variable nueva
df['Date'] = Date

#Vemos el resultado
df.dtypes
Periodo            object
TOT               float64
EMP               float64
CP                float64
ASA               float64
PdS               float64
FnR               float64
Date       datetime64[ns]
dtype: object

Vamos a guardar los meses y años por separados en el DataFrame. Para esto utilizamos DatetimeIndex (abreviamos dt) que nos permite extraer el segundos/dia/mes/año de una variable del tipo datetime, por ejemplo usando month y year.

#Guardamos el mes en una variable 
df['mes'] = df['Date'].dt.month

#Guardamos el año en una variable
df['year'] = df['Date'].dt.year

#Vemos el resultado
print(df.dtypes)
df.head()
Periodo            object
TOT               float64
EMP               float64
CP                float64
ASA               float64
PdS               float64
FnR               float64
Date       datetime64[ns]
mes                 int64
year                int64
dtype: object
Periodo TOT EMP CP ASA PdS FnR Date mes year
0 mar.2010 7156.21 318.32 1289.68 5141.76 325.38 81.08 2010-03-31 3 2010
1 abr.2010 7198.78 324.94 1332.33 5114.80 331.31 95.39 2010-04-30 4 2010
2 may.2010 7181.90 326.95 1346.54 5080.65 328.56 99.21 2010-05-31 5 2010
3 jun.2010 7221.58 328.03 1384.28 5074.00 327.60 107.68 2010-06-30 6 2010
4 jul.2010 7256.52 333.82 1390.03 5081.93 339.44 111.29 2010-07-31 7 2010

Podemos agrupar una variable mediante groupby. Luego podemos aplicar funciones básicas como mean(), std(), sum(), etc.

df_agrupado = df.groupby('year')
df_agrupado.mean()
TOT EMP CP ASA PdS FnR mes
year
2010 7318.352000 333.601000 1410.938000 5136.587000 334.501000 102.726000 7.5
2011 7676.545833 349.602500 1488.466667 5382.360833 359.097500 97.016667 6.5
2012 7858.610833 323.438333 1460.165833 5626.480000 353.575000 94.954167 6.5
2013 8023.047500 334.418333 1484.029167 5766.081667 336.561667 101.956667 6.5
2014 8150.107500 338.969167 1569.675000 5800.595833 336.685000 104.185000 6.5
2015 8273.739167 344.422500 1593.640833 5919.330833 319.370833 96.973333 6.5
2016 8391.921667 334.027500 1686.861667 5945.020000 330.251667 95.760000 6.5
2017 8574.332500 371.210833 1777.026667 6008.260833 323.455000 94.375833 6.5
2018 8773.940000 363.481667 1800.942500 6185.576667 323.193333 100.750000 6.5
2019 8953.679167 368.760000 1855.963333 6320.787500 319.305833 88.863333 6.5
2020 7932.822500 274.867500 1495.655000 5878.350000 214.355000 69.597500 6.5
2021 8145.750000 243.140000 1630.363333 5999.126667 200.213333 72.900000 2.0

Lo anterior podemos hacerlo sobre una o un grupo de variables específicas. Para esto hacemos lo siguiente:

  • Seleccionamos las variablers sobre las que vamos a trabajar mediante una lista: df[[‘TOT’, ‘year’]]. Tiene que estar la variable sobre la que quiero tener el análisis (TOT) y la que voy a agrupar (year).

  • Aplicamos la función para agrupar y la variable: groupby(‘year’).

df_agrupado = df[['TOT', 'year']].groupby('year')
df_agrupado.mean()
TOT
year
2010 7318.352000
2011 7676.545833
2012 7858.610833
2013 8023.047500
2014 8150.107500
2015 8273.739167
2016 8391.921667
2017 8574.332500
2018 8773.940000
2019 8953.679167
2020 7932.822500
2021 8145.750000

Si analizamos el resultado de df_agrupado.mean() tiene dos elementos:

  • index: variable sobre la que se agrupó, esta la llamamos con .index

  • Variables relevantes: sobre las que hicimos el análisis, en este caso TOT. La llamamos con [‘TOT’]

\[f(x) = 10\]
#Index
print("index:", df_agrupado.mean().index)

#TOT
print("TOT:", df_agrupado.mean()['TOT'])
index: Int64Index([2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020,
            2021],
           dtype='int64', name='year')
TOT: year
2010    7318.352000
2011    7676.545833
2012    7858.610833
2013    8023.047500
2014    8150.107500
2015    8273.739167
2016    8391.921667
2017    8574.332500
2018    8773.940000
2019    8953.679167
2020    7932.822500
2021    8145.750000
Name: TOT, dtype: float64
import matplotlib.pyplot as plt
x = df_agrupado.mean().index
y = df_agrupado.mean()['TOT']
plt.bar(x, y)
plt.show()
_images/Clase_13_14_a_52_0.png

Con esto podemos empezar a utilizar una base de datos y mostrar algunos resultados!

  • Para leer datos de excel podemos usar pd.read_excel.

  • Para leer datos de stata podemos usar pd.read_stata.

df_agrupado = df[['TOT', 'year']].groupby('year')
df_agrupado.mean()
TOT
year
2010 7318.352000
2011 7676.545833
2012 7858.610833
2013 8023.047500
2014 8150.107500
2015 8273.739167
2016 8391.921667
2017 8574.332500
2018 8773.940000
2019 8953.679167
2020 7932.822500
2021 8145.750000
df_agrupado = df[['TOT', 'year']].groupby('year')
# df_agrupado.head()

Clase 14: continuación con los datos

En esta clase vamos a continuar con el uso de pandas. Veremos:

  1. Consultas SQL

    • Qué es?

    • Select

    • Where

    • Join

    • Union

    • Update

    • Delete

  2. Ejercicios

1. Repaso

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

Para el ejemplo de esta clase vamos a usar una pequeña base llamada tips que tiene a disposición la librería pandas.

#Podemos llamar la librería con el siguiente url
url = ("https://raw.github.com/pandas-dev"
        "/pandas/master/pandas/tests/io/data/csv/tips.csv")


#Creamos el DataFrame
tips = pd.read_csv(url)
#Revisamos el contenido
tips.head(10)
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
5 25.29 4.71 Male No Sun Dinner 4
6 8.77 2.00 Male No Sun Dinner 2
7 26.88 3.12 Male No Sun Dinner 4
8 15.04 1.96 Male No Sun Dinner 2
9 14.78 3.23 Male No Sun Dinner 2

Vamos a crear una función que nos permita visualizar información del DataFrame

#Declaramos la función y el nombre del input
def grafico1(DF): 
    #Definimos los datos a usar
    data1 = DF[['total_bill', 'size']].groupby('size').mean()
    plt.bar(data1.index, data1.total_bill)
    plt.xlabel("Tamaño de la mesa")
    plt.ylabel("Promedio de la cuenta")    

#Llamamos la función y le damos el input
grafico1(tips)    
_images/Clase_13_14_b_6_0.png
#Declaramos la función y el nombre del input
def grafico2(DF): 
    #Definimos los datos
    x = DF.total_bill
    y = DF.tip
    #Gráfico de puntos (scatter)
    plt.scatter(x, y)
    plt.xlabel("Total cuenta")
    plt.ylabel("Propina")    
    
    #Gráfico de línea
    m, b = np.polyfit(x, y, 1)    #Sacar la pendiente y el intercepto de y(x)
    plt.plot(x, m*x + b, color='r')

#Llamamos la función y le damos el input
grafico2(tips) 
_images/Clase_13_14_b_7_0.png

Y si la función la queremos con dos input?

#Declaramos la función y el nombre del input
def grafico2(x, y): 
    #Gráfico de puntos (scatter)
    plt.scatter(x, y)
    plt.xlabel("Total cuenta")
    plt.ylabel("Propina")    
    
    #Gráfico de línea
    m, b = np.polyfit(x, y, 1)    #Sacar la pendiente y el intercepto de y(x)
    plt.plot(x, m*x + b, color='r')

#Creamos las variables que vamos a usar como input
x = tips.total_bill 
y = tips.tip
#Hacemos el gráfico
grafico2(x, y) 
_images/Clase_13_14_b_9_0.png

Si queremos guardar la diferencia entre las filas de una variable del set de datos, podemos por ejemplo usar un for

#Definimos la variable a utilizar
var = tips.total_bill
#Creamos la lista donde vamos a guardar los datos
lista = []
#Realizamos el lopp: desde el segundo valor hasta el último (1, n)
for i in range(1, len(var)): 
    lista.append(var[i]-var[i-1]) #Sacamos la diferencia

print(var[0:10])
print(lista[0:10])
0    16.99
1    10.34
2    21.01
3    23.68
4    24.59
5    25.29
6     8.77
7    26.88
8    15.04
9    14.78
Name: total_bill, dtype: float64
[-6.649999999999999, 10.670000000000002, 2.669999999999998, 0.9100000000000001, 0.6999999999999993, -16.52, 18.11, -11.84, -0.2599999999999998, -4.51]

Otra forma es usar la función diff. El siguiente link muestra en detalle cómo usar la función: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.diff.html

#si queremos sacar la diferencia de una variable definimos el DataFrame.nombre_variable.diff()
print(tips.total_bill.diff().head())

#Tambien podemos especificar los periodos para hacer la diferencia
print(tips.total_bill.diff(periods=3).head())

#Podemos cambiar el sentido de la diferencia (ascendente o descendente)
print(tips.total_bill.diff(periods=-3).head())
0      NaN
1    -6.65
2    10.67
3     2.67
4     0.91
Name: total_bill, dtype: float64
0      NaN
1      NaN
2      NaN
3     6.69
4    14.25
Name: total_bill, dtype: float64
0    -6.69
1   -14.25
2    -4.28
3    14.91
4    -2.29
Name: total_bill, dtype: float64

2. Consultas SQL

SQL (por su nombre en inglés) es un lenguaje de programación utilizado para trabajar con bases relacionales. Este lenguaje nos permite recuperar de forma simple información de bases de datos donde tenemos bases que pueden estar relacionadas mediante alguna variable. Por ejemplo:

_images/base_relacional.png

Podemos tener información de diferentes fuentes, planillas de costos, ingresos, compras, etc. y buscar vincular esta información para hacer análisis, presentar información o realizar cálculos específico.

El objetivo de esta clase no es SQL, sino la forma de trabajar este lenguaje. En python podemos usar pandas para replicar el trabajo con bases de datos relacionales.

Select

El objetivo es seleccionar un set de información específico

#Ponemos el DataFrame y las variables específicas que queremos
#Ojo que va "DataFrame[]" y dentro del paréntesis van las variables dentro de un "[]"
tips[["total_bill", "tip", "smoker", "time"]].head(5)
total_bill tip smoker time
0 16.99 1.01 No Dinner
1 10.34 1.66 No Dinner
2 21.01 3.50 No Dinner
3 23.68 3.31 No Dinner
4 24.59 3.61 No Dinner

Para agregar un nueva variable según un ratio podemos usar DataFrame.assign()

tips.assign(tip_rate=tips["tip"] / tips["total_bill"]).head(5)
total_bill tip sex smoker day time size tip_rate
0 16.99 1.01 Female No Sun Dinner 2 0.059447
1 10.34 1.66 Male No Sun Dinner 3 0.160542
2 21.01 3.50 Male No Sun Dinner 3 0.166587
3 23.68 3.31 Male No Sun Dinner 2 0.139780
4 24.59 3.61 Female No Sun Dinner 4 0.146808
Where

Usamos este tipo de consulta para seleccionar una información en específico, por ejemplo cuando la variable time es igual a Diner

#La sintaxis es DataFrame[DataFrame[Variable] == Condicion]
#En resumen le estamos diciendo DataFrame[Variable == Condicion], donde Variable=DataFrame[Variable]
tips[tips["time"] == "Dinner"].head(5)
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

Podemos ver si se cumple una condición como verdadero/falso y podemos usar la función value_counts() para ver cuántas veces se cumple o no

#Creamos una variable que guarde si se cumple la condición
variable_condicion = tips["time"] == "Dinner"

#Evaluamos cuantas veces se cumple la condición con la función value_counts()
variable_condicion.value_counts()
True     176
False     68
Name: time, dtype: int64

Luego, podemos revisar el DataFrame donde se cumpla la condición que establecimos previamente.

tips[variable_condicion].head(5)
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4

Podemos seleccionar más de una condición usando & (and) or | (or)

Por ejemplo, seleccionamos cuando time=Dinner y cuando tip>5.

#LLamamos el DataFrame[variable1 == condicion1 & variable2 >condicion2]
tips[(tips["time"] == "Dinner") & (tips["tip"] > 5.00)]
total_bill tip sex smoker day time size
23 39.42 7.58 Male No Sat Dinner 4
44 30.40 5.60 Male No Sun Dinner 4
47 32.40 6.00 Male No Sun Dinner 4
52 34.81 5.20 Female No Sun Dinner 4
59 48.27 6.73 Male No Sat Dinner 4
116 29.93 5.07 Male No Sun Dinner 4
155 29.85 5.14 Female No Sun Dinner 5
170 50.81 10.00 Male Yes Sat Dinner 3
172 7.25 5.15 Male Yes Sun Dinner 2
181 23.33 5.65 Male Yes Sun Dinner 2
183 23.17 6.50 Male Yes Sun Dinner 4
211 25.89 5.16 Male Yes Sat Dinner 4
212 48.33 9.00 Male No Sat Dinner 4
214 28.17 6.50 Female Yes Sat Dinner 3
239 29.03 5.92 Male No Sat Dinner 3
#La condición "o" la llamamos con el símbolo "|"" 
tips[(tips["size"] >= 5) | (tips["total_bill"] > 45)]
total_bill tip sex smoker day time size
59 48.27 6.73 Male No Sat Dinner 4
125 29.80 4.20 Female No Thur Lunch 6
141 34.30 6.70 Male No Thur Lunch 6
142 41.19 5.00 Male No Thur Lunch 5
143 27.05 5.00 Female No Thur Lunch 6
155 29.85 5.14 Female No Sun Dinner 5
156 48.17 5.00 Male No Sun Dinner 6
170 50.81 10.00 Male Yes Sat Dinner 3
182 45.35 3.50 Male Yes Sun Dinner 3
185 20.69 5.00 Male No Sun Dinner 5
187 30.46 2.00 Male Yes Sun Dinner 5
212 48.33 9.00 Male No Sat Dinner 4
216 28.15 3.00 Male Yes Sat Dinner 5

Muchas veces los datos vienen con información vacía o nula. En pandas existe el tipo NaN que significa Not a Number (valores faltantes). Para seleccionar este tipo de datos (o excluirlos) podemos usar isna() (o notna()).

Para ejemplificar lo anterio vamos a crear un pequeño dataframe con este tipo de datos con np.NaN.

ej_null = pd.DataFrame({"col1": ["A", "B", np.NaN, "C", "D"], "col2": ["F", np.NaN, "G", "H", "I"]})
ej_null.head()
col1 col2
0 A F
1 B NaN
2 NaN G
3 C H
4 D I

Seleccionamos el dataframe cuando la columna 2 tiene valores del tipo NaN

ej_null[ej_null["col2"].isna()]
col1 col2
1 B NaN

O cuando la columna 1 no tiene valores del tipo NaN

ej_null[ej_null["col1"].notna()]
col1 col2
0 A F
1 B NaN
3 C H
4 D I
Group by

Las clases anteriores vimos algunos ejemplos de como usar la función groupby(). Por ejemplo, si queremos agrupar por sexo y saber el número de tips podemos hacer lo siguiente

#Si agrupamos por sexo y nos cuenta todo el set de datos tendremos información irrelevante de sobra
print(tips.groupby("sex").count())
#Podemos seleccionar la información específica que queremos
print(tips[['tip', 'sex']].groupby("sex").count())
        total_bill  tip  smoker  day  time  size
sex                                             
Female          87   87      87   87    87    87
Male           157  157     157  157   157   157
        tip
sex        
Female   87
Male    157
#O una forma más simple
print(tips.groupby("sex").size())
sex
Female     87
Male      157
dtype: int64

Una diferencia importante entre estos dos métodos (count() vs size()) es que el count() se aplica sobre todas las variables y no incluye los not null, mientras que size() da el total. Si usamos el dataframe del ejemplo anterior:

print(ej_null.groupby('col1').size())
print(ej_null.groupby('col1').count())
col1
A    1
B    1
C    1
D    1
dtype: int64
      col2
col1      
A        1
B        0
C        1
D        1

Podemos hacer multiples operaciones usando la función agg(). Por ejemplo podemos agrupar por día, y que realice el promedio ‘tip’, junto con la cantidad de días.

La sintaxis es DataFrame.groupby(“Variable a agrupar).agg({“variable1”:operacion, “variable2”:operacion})

tips.groupby("day").agg({"tip": np.mean, "day": np.size})
tip day
day
Fri 2.734737 19
Sat 2.993103 87
Sun 3.255132 76
Thur 2.771452 62

Para agrupar por mas de una variable usamos una lista dentro del groupby y luego la operación que queremos con agg().

tips.groupby(["smoker", "day"]).agg({"tip": [np.size, np.mean]})
tip
size mean
smoker day
No Fri 4.0 2.812500
Sat 45.0 3.102889
Sun 57.0 3.167895
Thur 45.0 2.673778
Yes Fri 15.0 2.714000
Sat 42.0 2.875476
Sun 19.0 3.516842
Thur 17.0 3.030000
Join

Una función del tipo join se utiliza para juntar bases de datos mediante alguna variable en común.

Los tipos de join más usados son:

_images/join_sql.png

Para nuestro ejemplo vamos a crear dos DataFrame que van a compartir informacion y que vamos a ir vinculando según los diferentes tipos de join.

df1 = pd.DataFrame({"key": ["A", "B", "C", "D"], "value": np.random.randn(4)})
df2 = pd.DataFrame({"key": ["B", "D", "D", "E"], "value": np.random.randn(4)})
print(df1.head())
print(df2.head())
  key     value
0   A -0.187041
1   B -0.157632
2   C -0.535480
3   D  0.057931
  key     value
0   B -0.040069
1   D  0.592890
2   D  0.649470
3   E  0.023413
Inner Join

Juntamos dos Bases usando la llave en ambas tablas. En nuestro ejemplo la llave (key) que se encuentra en ambas tablas corresponden a: B y D. Entonces vamos a excluir A, C y E.

Sin embargo, en la tabla 2 la llave D está dos veces, por lo que nuestra tabla final va a incluir este resultado la cantidad de veces que se encuentre en la tabla 1 y tabla 2.

pd.merge(df1, df2, on="key")
key value_x value_y
0 B -0.157632 -0.040069
1 D 0.057931 0.592890
2 D 0.057931 0.649470
Left outher join

Este tipo de consulta se usa cuando queremos mostrar toda la información de una tabla y sólo la información de la segunda tabla cuando se condicen las llaves

pd.merge(df1, df2, on="key", how="left")
key value_x value_y
0 A -0.187041 NaN
1 B -0.157632 -0.040069
2 C -0.535480 NaN
3 D 0.057931 0.592890
4 D 0.057931 0.649470
Right Join

Nos muestra todo lo de la tabla de la derecha y sólo los valores de la izquierda cuando coincide la llave.

pd.merge(df1, df2, on="key", how="right")
key value_x value_y
0 B -0.157632 -0.040069
1 D 0.057931 0.592890
2 D 0.057931 0.649470
3 E NaN 0.023413
Full Join

Cuando queremos mostrar todos los datos de ambos dataframe

pd.merge(df1, df2, on="key", how="outer")
key value_x value_y
0 A -0.187041 NaN
1 B -0.157632 -0.040069
2 C -0.535480 NaN
3 D 0.057931 0.592890
4 D 0.057931 0.649470
5 E NaN 0.023413
Union

Usamos esta consulta cuando queremos agregar dos tablas o bases de datos.

Por ejemplo, si creamos dos DataFrames con ciudad y ranking en calidad de vida

df1 = pd.DataFrame({"ciudad": ["Valdivia", "Santiago", "Valparaiso"], "rank": range(1, 4)})
df2 = pd.DataFrame({"ciudad": ["Iquique", "Puerto Montt", "Valdivia"], "rank": [5, 4, 1]})
print(df1)
print(df2)
       ciudad  rank
0    Valdivia     1
1    Santiago     2
2  Valparaiso     3
         ciudad  rank
0       Iquique     5
1  Puerto Montt     4
2      Valdivia     1

Los podemos unir usando la función concat

pd.concat([df1, df2])
ciudad rank
0 Valdivia 1
1 Santiago 2
2 Valparaiso 3
0 Iquique 5
1 Puerto Montt 4
2 Valdivia 1

Para eliminar los duplicados podemos usar drop_duplicates

df3 = pd.concat([df1, df2]).drop_duplicates()
print(df3)
         ciudad  rank
0      Valdivia     1
1      Santiago     2
2    Valparaiso     3
0       Iquique     5
1  Puerto Montt     4
Update

Cuando queremos actualizar una variable debemos definir una condición y un nuevo valor. Esto lo podemos hacer para variables numéricas o variables de texto. Para esto usamos la función loc

df3.loc[df3["ciudad"] == "Puerto Montt", "ciudad"] = "Pto. Montt"
print(df3)
       ciudad  rank
0    Valdivia     1
1    Santiago     2
2  Valparaiso     3
0     Iquique     5
1  Pto. Montt     4
Delete

Para eliminar un valor también vamos a usar la función loc. Vamos a definir una condición que no se cumple (!=) y con ella excluir un valor en particular. Por ejemplo, podemos sacar de la tabla cuando la ciudad es Iquique.

df3 = df3.loc[df3["ciudad"] != "Iquique"] 
print(df3)
       ciudad  rank
0    Valdivia     1
1    Santiago     2
2  Valparaiso     3
1  Pto. Montt     4

3. Ejercicios

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
nombre = pd.read_excel("ejemplo_sql.xlsx", sheet_name='nombre', thousands=",")
edad = pd.read_excel("ejemplo_sql.xlsx", sheet_name='edad', thousands=",")
almuerzo = pd.read_excel("ejemplo_sql.xlsx", sheet_name='Almuerzo', thousands=",")
print(nombre.head())
print(edad.head())
print(almuerzo.head())
      Nombre  id
0       Juan   1
1     Andrea   2
2      Ramón   3
3  Valentina   4
   id  Edad
0   1    10
1   2    25
2   3    60
3   4    55
   id     dia  hora         almuerzo
0   1   Lunes    12  pastel de papas
1   2   Lunes    13          porotos
2   3   Lunes    14         lentejas
3   4   Lunes    12          pescado
4   1  Martes    14         ensalada
  1. Seleccionar del dataframe nombre cuando es igual a Valentina. Guardar el id en una variable id_valentina.

id = nombre[nombre["Nombre"] == "Valentina"].id
  1. Usando el dataframe de almuerzo agrupar por día=lunes y ver hora promedio de almuerzo.

df_lunes = almuerzo[almuerzo["dia"] == "Lunes"]
df_lunes[["dia", "hora"]].groupby("dia").mean()
hora
dia
Lunes 12.75
  1. Graficar el promedio de hora de almuerzo por día. Usar un gráfico de barras.

x = almuerzo[["dia", "hora"]].groupby("dia").mean().index
y = almuerzo[["dia", "hora"]].groupby("dia").mean()
plt.bar(x, y.hora)
plt.ylabel("Promedio hora de almuerzo")
plt.show()
_images/Clase_13_14_b_72_0.png
  1. Usando las funciones del tipo join ver:

    • ¿Qué almorzó Juan en la semana?

    • ¿A qué hora y qué almorzó Andrea el martes?

    • Crear dataframe df_total con las variables id, Nombre, Edad, dia, hora y almuerzo.

#Inner join: Almuerzo de juan
df = pd.merge(nombre, almuerzo, on="id", how="outer")
df_juan = df[["Nombre", "almuerzo"]][df.Nombre == "Juan"]
print(df_juan)


#Andrea
df_andrea = df[["Nombre", "almuerzo"]][(df.Nombre == "Andrea") & (df.dia=="Martes")]
print(df_andrea)
      
#Data Frame total
df_total = pd.merge(nombre, edad, on="id", how="outer")
df_total = pd.merge(df_total, almuerzo, on="id",how="outer") 
print(df_total)
  Nombre         almuerzo
0   Juan  pastel de papas
1   Juan         ensalada
2   Juan        garbanzos
   Nombre almuerzo
4  Andrea      NaN
       Nombre  id  Edad        dia  hora         almuerzo
0        Juan   1    10      Lunes    12  pastel de papas
1        Juan   1    10     Martes    14         ensalada
2        Juan   1    10  Miércoles    16        garbanzos
3      Andrea   2    25      Lunes    13          porotos
4      Andrea   2    25     Martes    14              NaN
5      Andrea   2    25  Miércoles    13          cazuela
6       Ramón   3    60      Lunes    14         lentejas
7       Ramón   3    60     Martes    13            asado
8       Ramón   3    60  Miércoles    14              NaN
9   Valentina   4    55      Lunes    12          pescado
10  Valentina   4    55     Martes    15              NaN
11  Valentina   4    55  Miércoles    14              NaN
  1. ¿Cuántas observaciones tiene el df_total?

df_total.id.count()
12
  1. ¿Qué variables tienen NaN y cuántos NaN hay?

#Variables que tienen NaN
#Definimos una variable que evalúe si una variable tiene NaN -> print esta variable tiene NaN
def NA(df): 
    if df.Nombre.isna().any() == True: #Ocupamos la función any() para comparar cada valor de la variable igual a la condición
        print("Nombre tiene NaN")
    elif df.id.isna().any() == True: 
        print("id tiene NaN")
    elif df.Edad.isna().any() == True: 
        print("Edad tiene NaN")        
    elif df.id.isna().any() == True: 
        print("id tiene NaN")
    elif df.dia.isna().any() == True: 
        print("dia tiene NaN")        
    elif df.hora.isna().any() == True: 
        print("hora tiene NaN")           
    elif df.almuerzo.isna().any() == True: 
        print("almuerzo tiene NaN")   
        
#Evaluamos la función        
NA(df_total)

#Cuántos NaN? 
#Sabemos que sólo almuerzo tiene NaN
print("La variable almuerzo tiene " + str(df_total[df_total["almuerzo"].isna()].id.count()) + " NaN." )
almuerzo tiene NaN
La variable almuerzo tiene 4 NaN.
  1. Crear un nuevo dataframe excluyendo los NaN

condicion = df_total.almuerzo.notna() == True
df_sinNaN = df_total[condicion]
print(df_sinNaN)
      Nombre  id  Edad        dia  hora         almuerzo
0       Juan   1    10      Lunes    12  pastel de papas
1       Juan   1    10     Martes    14         ensalada
2       Juan   1    10  Miércoles    16        garbanzos
3     Andrea   2    25      Lunes    13          porotos
5     Andrea   2    25  Miércoles    13          cazuela
6      Ramón   3    60      Lunes    14         lentejas
7      Ramón   3    60     Martes    13            asado
9  Valentina   4    55      Lunes    12          pescado

Clase 15: gráficos pro con Plotly

En esta clase usaremos la libería plotly para ver visualizaciones y tablas. Plotly es un paquete potente que permite realizar gráficos interactivos de gran calidad visual. Veremos:

  • Cómo instalar

  • Tablas

  • Gráfico de líneas

  • Gráfico de puntos (scatter)

  • Gráfico de barras

  • Gráfico de torta

  • Gráfico de burbujas

  • Mapas de calor

  • Histogramas

  • Gráficos de error

  • Bráfico de cajas

1. Introducción

La librería plotly es una librería de python open-source que soporta más de 40 tipos de gráficos interactivos, en el ámbito estadístico, financiero, geográfico, científico, etc.

Esta librería requiere algunos pasos adicionales en su instalación con respecto a otras librerías.

El detalle de la instalación lo pueden encontrar en el siguiente link: https://plotly.com/python/getting-started/

Pasos para instalar plotly:

  1. Abrir el Compand Prompt en Anaconda (pantalla negra). Los siguientes códigos los debe colocar en esta terminal.

  2. Pueden instalar mediante pip o conda:

    • pip install plotly==4.14.3

    • conda install -c plotly plotly=4.14.3

  3. Si van a trabajar desde Jupyter Notebook deben instlar los siguientes paquetes adicionales usando pip o conda:

    • pip install “notebook>=5.3” “ipywidgets>=7.5”

    • conda install “notebook>=5.3” “ipywidgets>=7.5”

  4. Si trabajan con JupyterLab deben instalar usando pip o conda:

    • pip install jupyterlab “ipywidgets>=7.5”

    • conda install jupyterlab “ipywidgets>=7.5”

  5. Instalar nodejs:

    • Chequear si tiene instalado el programa. En el Command Prompt ejecutar node -v, le va a mostrar si tiene instalado el programa y qué versión tiene. Si lo tiene instalado saltar los siguientes dos pasos.

    • Descargar el instalador para su sistema operativo: https://nodejs.org/es/download/

    • Ejecutar el archivo instalado

  6. Una vez tenga chequeado que tiene instalado el nodejs ejecutar:

    • jupyter labextension install jupyterlab-plotly@4.14.3

    • Ver si la instalación arroja algún error!

Una vez realizados estos 6 pasos, debería poder ejecutar el siguiente código en un JupyterLab y le debería mostrar un gráfico de barras.

import plotly.graph_objects as go
fig = go.Figure(data=go.Bar(y=[2, 3, 1]))
fig.show()

2. Tablas

go.Table nos va a entragar un interfaz para visualizar datos mediante tablas, en formato de filas-columnas. Estas tablas las podemos enchular según las necesidades del caso.

#Importar librería
import plotly.graph_objects as go

#Valores a mostrar 
encabezado = ['A Scores', 'B Scores']
valores = [[100, 90, 80, 90], [95, 85, 75, 95]]

#Crear figura
#1. llamar librería y función: go.Figure()
#2. Definir datos: data=[]
#3. Usar go.Table() para definir encabezado (header) y valores(cells)
#4. Guardamos el resultado en una variable -> alpicamos fig.show()
fig = go.Figure(data=[go.Table(header=dict(values=encabezado),
                               cells=dict(values=valores))
                     ])
fig.show()

Podemos arreglar

fig = go.Figure(data=[go.Table(
    #Encabezado
    header=dict(values=encabezado,
                line_color='red', #Color borde: cambiar rojo
                fill_color='lightskyblue', #Color fondo
                align='left'), #Alineación
    #Valores
    cells=dict(values=valores,
               line_color='darkslategray',#Color borde
               fill_color='lightcyan', #Color rojo
               align='left')) #Alineación
])

#Una vez creado el fig podemos editar su tamaño
fig.update_layout(width=300, height=300)
fig.show()

Para modificar el formato de los datos, ejemplo número de decimales, podemos usar format

encabezado = ['A Scores', 'B Scores', 'C Scores']
valores = [[100, 90, 80, 90], [95, 85, 75, 95], [95.12345, 85.12345, 75.12345, 95.12345]]

fig = go.Figure(data=[go.Table(
    #Encabezado
    header=dict(values=encabezado,
                line_color='darkslategray', #Color borde: cambiar rojo
                fill_color='lightskyblue', #Color fondo
                align='left'), #Alineación
    #Valores
    cells=dict(values=valores,
               line_color='darkslategray',#Color borde
               fill_color='lightcyan', #Color rojo
               align='left', #Alineación
               format = [None, ".1f", ".2f"])) #Formato: en una lista va según el número de columnas
                ])

#Una vez creado el fig podemos editar su tamaño
fig.update_layout(width=400, height=300)
fig.show()

3. Gráficos de líneas

Para este ejemplo vamos a usar la librería plotly.express. Dentro de esta tenemos el DataFrame gapminder datos de expectativa de vida, población, PIB por país.

#Importamos la librería
import plotly.express as px

#Usamos un subconjunto de los datos, seleccionamos América
df = px.data.gapminder().query("continent=='Americas'")
df.head()
country continent year lifeExp pop gdpPercap iso_alpha iso_num
48 Argentina Americas 1952 62.485 17876956 5911.315053 ARG 32
49 Argentina Americas 1957 64.399 19610538 6856.856212 ARG 32
50 Argentina Americas 1962 65.142 21283783 7133.166023 ARG 32
51 Argentina Americas 1967 65.634 22934225 8052.953021 ARG 32
52 Argentina Americas 1972 67.065 24779799 9443.038526 ARG 32
fig = px.line(df, x="year", y="lifeExp", color='country')
fig.show()
df_sur = df[(df.country=="Argentina" )| (df.country=="Chile") | (df.country=="Uruguay")]
fig = px.line(df_sur, x="year", y="gdpPercap", color='country')
fig.show()

Usamos plotly.express cuando hacemos un ejercicio sencillo. Para comenzar a ejemplos más genéricos o en algunos casos, más complejos, vamos a usar go.Scatter

Como primer ejemplo, vamos a usar una función de los DataFrame llamada pivot_table que nos va a permitir modificar nuestra serie del tipo [Fecha, PIB, Pais] a [Fecha, PIB, Argentina, Chile, Uruguay].

df.head()
country continent year lifeExp pop gdpPercap iso_alpha iso_num
48 Argentina Americas 1952 62.485 17876956 5911.315053 ARG 32
49 Argentina Americas 1957 64.399 19610538 6856.856212 ARG 32
50 Argentina Americas 1962 65.142 21283783 7133.166023 ARG 32
51 Argentina Americas 1967 65.634 22934225 8052.953021 ARG 32
52 Argentina Americas 1972 67.065 24779799 9443.038526 ARG 32
import pandas as pd
df_sur2 = pd.pivot_table(df_sur, values=["gdpPercap"], index=["year"], columns="country" )
df_sur2.head()
gdpPercap
country Argentina Chile Uruguay
year
1952 5911.315053 3939.978789 5716.766744
1957 6856.856212 4315.622723 6150.772969
1962 7133.166023 4519.094331 5603.357717
1967 8052.953021 5106.654313 5444.619620
1972 9443.038526 5494.024437 5703.408898
#Importamos la librería
import plotly.graph_objects as go

#Creamos figura
fig = go.Figure()
#Primer plot

#go.Scatter(x=variable_x, y=variable_y)
fig.add_trace(go.Scatter(x=df_sur2.index, y=df_sur2.gdpPercap.Argentina,
                    mode='lines',
                    name='Argentina'))

#Segundo plot
fig.add_trace(go.Scatter(x=df_sur2.index, y=df_sur2.gdpPercap.Chile,
                    mode='lines+markers',
                    name='Chile'))

# #Tercer plot
fig.add_trace(go.Scatter(x=df_sur2.index, y=df_sur2.gdpPercap.Uruguay,
                    mode='markers', name='Uruguay'))

fig.show()

4. Gráfico de puntos (scatter)

Para un gráfico simple podemos volver a usar plotly.express

#Definimos el tipo gráfico
fig = px.scatter(df_sur, x="gdpPercap", y="lifeExp", color = "country")
fig.show()

Podemos agregar una tercera dimensión, representada en el tamaño de la burbuja. Para esto usamos el argumento size y podemos transformlo en un gráfico de burbujas.

#Definimos el tipo gráfico
fig = px.scatter(df_sur, x="gdpPercap", y="lifeExp", color = "country", size="pop")
fig.show()

Otro ejemplo es agregar una escala de colores para resaltar una categoría

import plotly.graph_objects as go
import numpy as np

df_chile = df[(df.country=="Chile")]

fig = go.Figure(data=go.Scatter(
    x = df_chile.gdpPercap,
    y = df_chile.lifeExp,
    mode='markers',
    marker=dict(
        size=16,
        color=df_chile.lifeExp, #set color equal to a variable
        colorscale='Viridis', # one of plotly colorscales
        showscale=True
    )
))

fig.show()

5. Gráfico de barras

fig = px.bar(df_chile, x='year', y='pop')
fig.show()
df_total = px.data.gapminder()
df_2007 = df_total[df_total.year==2007]
df_2007.head()
country continent year lifeExp pop gdpPercap iso_alpha iso_num
11 Afghanistan Asia 2007 43.828 31889923 974.580338 AFG 4
23 Albania Europe 2007 76.423 3600523 5937.029526 ALB 8
35 Algeria Africa 2007 72.301 33333216 6223.367465 DZA 12
47 Angola Africa 2007 42.731 12420476 4797.231267 AGO 24
59 Argentina Americas 2007 75.320 40301927 12779.379640 ARG 32
fig = px.bar(df_2007, x="continent", y="pop", title="Wide-Form Input")
fig.show()

6. Gráfico de torta

df.loc[df['pop'] < 8.e6, 'country'] = 'Other countries' # Represent only large countries
fig = px.pie(df, values='pop', names='country', title='Población en América')
fig.show()

7. Animación

df.head()
country continent year lifeExp pop gdpPercap iso_alpha iso_num
48 Argentina Americas 1952 62.485 17876956 5911.315053 ARG 32
49 Argentina Americas 1957 64.399 19610538 6856.856212 ARG 32
50 Argentina Americas 1962 65.142 21283783 7133.166023 ARG 32
51 Argentina Americas 1967 65.634 22934225 8052.953021 ARG 32
52 Argentina Americas 1972 67.065 24779799 9443.038526 ARG 32
import plotly.express as px
df = px.data.gapminder()

fig = px.scatter(df, x="gdpPercap", y="lifeExp", 
                 animation_frame="year", 
                 animation_group="country",
                 size="pop", 
                 color="continent", 
                 hover_name="country", 
                 facet_col="continent",
                 log_x=True, size_max=45, range_x=[100,100000], range_y=[25,90]
                )
fig.show()
fig = px.bar(df, x="continent", y="pop", 
             color="continent",
             animation_frame="year", 
             animation_group="country", 
             range_y=[0,4000000000])
fig.show()

8. Mapa de calor

df = px.data.gapminder().query("continent=='Americas'")
df.head()
country continent year lifeExp pop gdpPercap iso_alpha iso_num
48 Argentina Americas 1952 62.485 17876956 5911.315053 ARG 32
49 Argentina Americas 1957 64.399 19610538 6856.856212 ARG 32
50 Argentina Americas 1962 65.142 21283783 7133.166023 ARG 32
51 Argentina Americas 1967 65.634 22934225 8052.953021 ARG 32
52 Argentina Americas 1972 67.065 24779799 9443.038526 ARG 32
fig = go.Figure(data=go.Heatmap(
        z=df.lifeExp,
        x=df.year,
        y=df.country,
        colorscale='Viridis'
        ))

fig.update_layout(
    title='Expectativa de vida',
    xaxis_nticks=13)

fig.update_layout(width=750, height=750)
fig.show()

9. sunburst charts

import plotly.express as px

df = px.data.gapminder().query("year == 2007")


fig = px.sunburst(df, path=['continent', 'country'], values='pop', color='lifeExp',
                  hover_data=['iso_alpha'])
fig.show()

10. Treemap

df.head()
country continent year lifeExp pop gdpPercap iso_alpha iso_num
11 Afghanistan Asia 2007 43.828 31889923 974.580338 AFG 4
23 Albania Europe 2007 76.423 3600523 5937.029526 ALB 8
35 Algeria Africa 2007 72.301 33333216 6223.367465 DZA 12
47 Angola Africa 2007 42.731 12420476 4797.231267 AGO 24
59 Argentina Americas 2007 75.320 40301927 12779.379640 ARG 32
import plotly.express as px
import numpy as np
df = px.data.gapminder().query("year == 2007")

fig = px.treemap(df, path=[px.Constant('world'), 'continent', 'country'], values='pop',  
                  hover_data=['iso_alpha'])
fig.show()

11. Histograma

import plotly.express as px
df = px.data.tips()
fig = px.histogram(df, x="total_bill", y="tip", color="sex", marginal="rug", hover_data=df.columns)
fig.show()

12. box plots

import plotly.express as px
df = px.data.tips()
fig = px.box(df, x="day", y="total_bill", color="smoker", notched=True)
fig.show()

Actividad

Instrucciones:

  • Cada pregunta la debe responder en una celda diferente (en total 4 celdas de código).

  • Antes de cada celda de código debe ir un markdown que muestre la pregunta que está respondiendo.

  • En la celda de código debe agregar un breve comentario de qué está haciendo.

Revisar las bases y ver como vienen. Limpiar (en excel) para que sea sencillo importar los datos. Por ejemplo, en la base de EMPLEO las primeras 5 filas son celdas vacías o título de la hoja. Eso se puede borrar. Las columnas “C”, “E” y otras son columnas en blanco, eliminar para que no queden a la hora de importar. La fila 7 tiene un subtítulo “en miles” que no aporta, también se puede eliminar. La idea es que las bases queden sólo con las filas y columnas que va a importar a python.

Utilizando pandas importar las 4 bases, llamar: DF_IMACEC, DF_EMP, DF_IMP, DF_EXP. Limpiar para que se puedan trabajar (cambiar los nombres de variables, chequear el tipo de datos, revisar en qué fecha parten).

  1. Crear un DataFrame que tenga la variación porcentual anual de personas para las categorías independientes (total) y asalariados (total).

  2. Usando el DataFrame del IMACEC, transponer para que las fechas queden en las filas y las categorías como columnas. Para esto puede usar la función de pandas pivot_table.

  3. Al DF del punto 1), agregar la variación porcentual anual del imacec del punto 2), usando un inner join.

  4. Utilizando plotly, crear un subplot que contenga:

    1. Gráfico con la variación anual de independientes, asalariados e IMACEC. Debe usar líneas, markes o dash para diferenciar las tres líneas.

    2. Usando los DataFrame de exportaciones (EXP) e importaciones (IMP), crear un gráfico de scatter que tenga el total de exportaciones en el eje horizontal y el total de importaciones (CIF) en el vertical. Agregue al gráfico una línea de 45°.

    3. Dentro de las exportaciones seleccione: total exportaciones, minería,  agropecuario-silvícola y pesquero, industriales. Utilizando la primera fecha (enero-2009) como base 100, crear un mapa de calor que en el eje horizontal tenga las fechas y en el vertical las 4 categorías. Cambie la escala de colores (no utilizar el por defecto).

Cada gráfico debe tener un subtítulo, nombre de ejes y una leyenda -según corresponda-. hint: la clave es que ordene bien los datos antes del gráfico. Luego piense bien a qué debe llegar.

Clase 17: Introducción al análisis de texto (text mining)

En esta clase veremos una introducción al procesamiento de texto. Para esto utilizaremos un discurso presidencial y veremos como depurar este texto, de manera que podamos extraer información relevante.

Vamos a

  • Examinar el texto

  • Depurar:

    • Remover símbolos

    • Remover números

    • Mayúscula-minúscula

    • Espacios en blanco

    • Tíldes

  • Tokenizar

  • Frecuencias de palabras

  • Stopwords

  • Wordclouds

  • Stemming

  • N-gramas

1. Introducción

En el mundo moderno tenemos muchas fuentes de información, la tarea es cómo la recopilamos, procesamos y analizamos. Por ejemplo, las frases, textos, libros, comentarios en redes sociales, son una amplia gama de fuentes de información. El text mining es “obtener información interesante y no trivial de textos o información no estructurada”.

Ejemplos de información que podemos recopilar y procesar:

  • Noticias de diarios electrónicos

  • Libros

  • Twitter

  • etc.

Para efectos de esta clase, veremos la librería nltk (Natural Language Toolkit), que proporciona una potente herramienta de manejo de texto.

# Tratamiento de datos
# ==============================================================================
import numpy as np
import pandas as pd
import string
import re
import nltk
# nltk.download('punkt')
# import nltk.corpus
# from nltk.tokenize import word_tokenize

Vamos a importar un archivo .txt

#Discurso 1
# discurso1 = open('C:\\Users\\ordon\\Dropbox\\Computational_Economics\\Intro_python\\Data_text_proc\\discurso1.txt', 'r') 
discurso1 = open('/home/felix/Dropbox/Computational_Economics/Intro_python/Data_text_proc/discurso1.txt', 'r', encoding="latin-1") 




discurso1 = discurso1.read()
discurso1
'S.E. la Presidenta de la República, Michelle Bachelet, asiste al lanzamiento de la campaña \x93Chile necesita más médicos y especialistas. Incorpórate al Sistema Público de Salud\x94\nAmigas y amigos:\n\nQué duda cabe, la verdad, como señala esta campaña del Ministerio de Salud, que \x93Chile necesita más Médicos y Especialistas\x94 en el sector público. Y es una necesidad de nuestra sociedad, y estamos dando pasos concretos para poder satisfacer esta necesidad.\n\nY uno de los grandes objetivos que tenemos como Gobierno es, justamente, mejorar la atención de salud que recibe nuestra población.\n\nY una de nuestras preocupaciones centrales es justamente fortalecer y asegurar la capacidad del Estado para responder de manera más rápida y más integral a las necesidades de millones de nuestros compatriotas que utilizan la red pública de consultorios, hospitales, centros de salud familiar y los servicios de urgencia. \n\nLa calidad de vida en nuestro país depende fuertemente de la capacidad que tengamos para, obviamente, prevenir y para asegurar, yo diría, la dignidad y el tratamiento oportuno de las enfermedades que siempre son un duro golpe para las personas y para las familias. \n\nY en este proceso, el rol de los y las profesionales de la salud es fundamental. Para enfrentar las carencias que vive nuestro sistema de salud en este ámbito, como Gobierno pusimos en marcha, desde su inicio, el Plan de Ingreso, Formación y Retención de Médicos Especialistas.\nEste Plan nos permitirá sumar, durante estos cuatro años, mil 100 médicos a la Atención Primaria de Salud y formar 4 mil especialistas, médicos y odontólogos. \n\nY ya hemos obtenido algunos avances importantes en este sentido. Este año, gracias a la ampliación en 370 cupos adicionales cada año para la Etapa de Destinación y Formación, tanto rural como urbana, pudimos contar con 538 nuevos médicos en la Atención Primaria. \n\nY para mí fue bien interesante, doctor Concha, que cuando fui a Puyehue, de los tres médicos que estaban en el CESFAM, dos eran justamente de estos médicos que habían salido con cupos adicionales para la atención primaria. \n\nEs decir, los que corresponden a la ampliación generada por este Gobierno, más los cupos que tenía anteriormente este programa que -los que ya tenemos algunos añitos- conocemos como médicos generales de zona, y que en julio de este año se celebró el aniversario número 60.\n\nLa verdad, yo creo que se trata de una experiencia, desde mi punto de vista, inigualable en la formación de un o una joven profesional de la medicina. \n\nLa tarea de los antiguos médicos generales de zona (o en Etapa de Destinación y Formación) en esas comunidades, como las veíamos en el video, donde hay que llegar en lancha, donde en general las vías de comunicación son escasas -bueno, no muy lejos de aquí, yo me acuerdo, conversando con algún médico general de zona, siendo la epidemióloga del Servicio de Salud Occidente, tenía que ir en Curacaví, pero tenía que subir a caballo a veces a ver un paciente que vivía en algún lado. O sea, que trae un poquito de aventura, trae un poquito de aventura, pero también trae un trabajo intenso, arduo, pero que también es, yo diría, algo que genera una experiencia extraordinaria-, y en general son lugares donde las vías de comunicación muchas veces son escasas, y creo que es un valor que trasciende mucho más allá del tiempo que dura su destinación. \n\nCuando recorro el país, veo el trabajo de los médicos en las localidades del sur, del norte, el cariño que le tienen los vecinos y vecinas; escucho también los recuerdos y recibo los saludos para antiguos médicos generales de zona que con el tiempo han tomado otros rumbos. Estuvimos inaugurando en Paine, me parece que fue, el CESFAM Miguel Ángel Solar, y yo pensé que era el que yo conocía, el que había sido presidente de la FEUC, pero no, era su papá, que había partido ahí, se había especializado, había estado en Estados Unidos y después volvió ahí, hasta que falleció, y trabajó en esa misma localidad. La gente lo recordaba con mucho cariño y era muy emocionante ver los videos de los pacientes que había tenido. \n\nY con la ampliación de cupos en la Etapa de Destinación y Formación, al año 2017 se habrán agregado mil 100 nuevos médicos en la Atención Primaria y en abril de 2018 esta cifra va a llegar a mil 480.\n\nEntonces, vamos a pasar de una situación en la que existía un médico cada 4 mil 300 habitantes -cuando llegamos al gobierno, era la situación que nos encontramos; un médico cada 4 mil 300, a comienzos del 2014- a otra donde habrá un médico por cada 2 mil 800 habitantes, es decir, casi el doble. Todavía nos va a faltar un poquito para lo que son los estándares de la OECD, que son alrededor de 2 mil 100 o 2 mil 200 médicos por ese número de habitantes. Entonces. Esto, obviamente, el llegar a bajar de 4 mil 300 a 2 mil 800 habitantes por médico, creo que nos acerca a los estándares de los países desarrollados. \n\nAdemás, y aquí también aprovecho de agradecer el trabajo que ha hecho el Ministerio con las universidades, con la Asociación de Facultades de Medicina de Chile, con la Asociación Chilena de Enseñanza de Odontología, con el Colegio Médico y con el Colegio de Cirujanos Dentistas, se logró ampliar en un 40% los cupos disponibles para la formación de especialistas.\n\n¡40%! Incluso algunas universidades llegaron a un 100% en la ampliación de sus cupos, y creo que es una tremenda noticia para el avance de la salud en nuestro país.\n\nY éste es un ámbito en el que vamos a continuar poniendo los máximos esfuerzos, ya que existe un importante déficit de especialistas en nuestro país (se estima que son alrededor de 3 mil 800), que afecta directamente, por cierto, la calidad de la atención que reciben los pacientes del sistema público de salud.\n\nLos y las jóvenes egresados de la carrera de medicina tienen mucho interés de participar en estos programas y aproximadamente un 60% de ellos postula a las becas de especialización.\n\nPero queremos que sean todavía más y esperamos que en los próximos años esta cifra llegue al 80%. \n\nAsí, tanto a través de la Ampliación de la Etapa de Destinación y Formación como mediante el aumento de cupos de especialización, iremos disminuyendo las carencias que afectan a la atención pública de salud.\n\nPero, al mismo tiempo, nos permitirá entregar a más profesionales la posibilidad de vivir esta experiencia única, de valor incomparable, que es ejercer el objetivo social de la medicina, llegar a lugares apartados de nuestra patria, conocer realidades diversas, también.\n\nE integrarse a la red más compleja y amplia de atención médica como es el sistema público de salud, donde van a poder formarse de manera continúa a lo largo de su carrera profesional y de aprender de los y las mejores profesionales.\n\nPorque, tal como dice la campaña, \x93Chile necesita más médicos y especialistas\x94, los médicos y especialistas necesitan conocer y valorar las oportunidades que ofrece el Sistema Público de Salud.\n\nPor eso que esta campaña de difusión llegará a todas las facultades de medicina de nuestro país, donde, estoy segura, hay una enorme cantidad de vocaciones de servicio público que esperan aportar al desarrollo de nuestra patria y a la calidad de vida de nuestros compatriotas.\n\nUstedes que están acá en un hospital público, están ejerciendo esto que es tan importante. Queremos motivar a muchos jóvenes más para que la salud pública que ellos consideren seriamente. Y estoy segura que van a sentir todas las situaciones que vivimos los médicos, pero también toda aquella experiencia, de verdad, extraordinaria que significa estar trabajando para la gente. \n\nMuchas gracias.'

Examinar texto

discurso1[0:1000]
'S.E. la Presidenta de la República, Michelle Bachelet, asiste al lanzamiento de la campaña \x93Chile necesita más médicos y especialistas. Incorpórate al Sistema Público de Salud\x94\nAmigas y amigos:\n\nQué duda cabe, la verdad, como señala esta campaña del Ministerio de Salud, que \x93Chile necesita más Médicos y Especialistas\x94 en el sector público. Y es una necesidad de nuestra sociedad, y estamos dando pasos concretos para poder satisfacer esta necesidad.\n\nY uno de los grandes objetivos que tenemos como Gobierno es, justamente, mejorar la atención de salud que recibe nuestra población.\n\nY una de nuestras preocupaciones centrales es justamente fortalecer y asegurar la capacidad del Estado para responder de manera más rápida y más integral a las necesidades de millones de nuestros compatriotas que utilizan la red pública de consultorios, hospitales, centros de salud familiar y los servicios de urgencia. \n\nLa calidad de vida en nuestro país depende fuertemente de la capacidad que tengamos para, o'

2. Depurar

El proceso de depurar implica remover o cambiar elementos que no son palabras y que para efectos del análisis del texto puedan ser irrelevantes.

Vamos a:

  • Remover símbolos: Los textos pueden venir con variados símbolos como “,+,,<,@, entre otros.

  • Remover números: hay que evaluar si los números van a ser relevantes a la hora del texto que queremos analizar. En caso que no lo sean, los vamos a quitar.

  • Llevar a minúscula: Hola y hola, para efectos del análisis de texto son equivalentes. Por eso llevamos todo a minúsculas.

  • Eliminar espacios en blanco innecesarios: vamos a borrar cuando hay más de un espacio.

  • Eliminar tildes: para el análisis de texto podemos eliminar las tildes, para que palabras como analisis y análisis sean equivalentes.

  • Borrar saltos de línea.

Remover Símbolos
import re
regex = '[\\!\\\\\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\-\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\\\\\]\\^_\\`\\{\\|\\}\\~]'
nuevo_discurso = re.sub(regex, ' ', discurso1)
print(nuevo_discurso[0:2000])
S E  la Presidenta de la República  Michelle Bachelet  asiste al lanzamiento de la campaña “Chile necesita más médicos y especialistas  Incorpórate al Sistema Público de Salud”
Amigas y amigos 

Qué duda cabe  la verdad  como señala esta campaña del Ministerio de Salud  que “Chile necesita más Médicos y Especialistas” en el sector público  Y es una necesidad de nuestra sociedad  y estamos dando pasos concretos para poder satisfacer esta necesidad 

Y uno de los grandes objetivos que tenemos como Gobierno es  justamente  mejorar la atención de salud que recibe nuestra población 

Y una de nuestras preocupaciones centrales es justamente fortalecer y asegurar la capacidad del Estado para responder de manera más rápida y más integral a las necesidades de millones de nuestros compatriotas que utilizan la red pública de consultorios  hospitales  centros de salud familiar y los servicios de urgencia  

La calidad de vida en nuestro país depende fuertemente de la capacidad que tengamos para  obviamente  prevenir y para asegurar  yo diría  la dignidad y el tratamiento oportuno de las enfermedades que siempre son un duro golpe para las personas y para las familias  

Y en este proceso  el rol de los y las profesionales de la salud es fundamental  Para enfrentar las carencias que vive nuestro sistema de salud en este ámbito  como Gobierno pusimos en marcha  desde su inicio  el Plan de Ingreso  Formación y Retención de Médicos Especialistas 
Este Plan nos permitirá sumar  durante estos cuatro años  mil 100 médicos a la Atención Primaria de Salud y formar 4 mil especialistas  médicos y odontólogos  

Y ya hemos obtenido algunos avances importantes en este sentido  Este año  gracias a la ampliación en 370 cupos adicionales cada año para la Etapa de Destinación y Formación  tanto rural como urbana  pudimos contar con 538 nuevos médicos en la Atención Primaria  

Y para mí fue bien interesante  doctor Concha  que cuando fui a Puyehue  de los tres médicos que estaban en el CESFAM  d
Remover números
nuevo_discurso = re.sub('\d+', ' ', nuevo_discurso)
print(nuevo_discurso[0:2000])
S E  la Presidenta de la República  Michelle Bachelet  asiste al lanzamiento de la campaña “Chile necesita más médicos y especialistas  Incorpórate al Sistema Público de Salud”
Amigas y amigos 

Qué duda cabe  la verdad  como señala esta campaña del Ministerio de Salud  que “Chile necesita más Médicos y Especialistas” en el sector público  Y es una necesidad de nuestra sociedad  y estamos dando pasos concretos para poder satisfacer esta necesidad 

Y uno de los grandes objetivos que tenemos como Gobierno es  justamente  mejorar la atención de salud que recibe nuestra población 

Y una de nuestras preocupaciones centrales es justamente fortalecer y asegurar la capacidad del Estado para responder de manera más rápida y más integral a las necesidades de millones de nuestros compatriotas que utilizan la red pública de consultorios  hospitales  centros de salud familiar y los servicios de urgencia  

La calidad de vida en nuestro país depende fuertemente de la capacidad que tengamos para  obviamente  prevenir y para asegurar  yo diría  la dignidad y el tratamiento oportuno de las enfermedades que siempre son un duro golpe para las personas y para las familias  

Y en este proceso  el rol de los y las profesionales de la salud es fundamental  Para enfrentar las carencias que vive nuestro sistema de salud en este ámbito  como Gobierno pusimos en marcha  desde su inicio  el Plan de Ingreso  Formación y Retención de Médicos Especialistas 
Este Plan nos permitirá sumar  durante estos cuatro años  mil   médicos a la Atención Primaria de Salud y formar   mil especialistas  médicos y odontólogos  

Y ya hemos obtenido algunos avances importantes en este sentido  Este año  gracias a la ampliación en   cupos adicionales cada año para la Etapa de Destinación y Formación  tanto rural como urbana  pudimos contar con   nuevos médicos en la Atención Primaria  

Y para mí fue bien interesante  doctor Concha  que cuando fui a Puyehue  de los tres médicos que estaban en el CESFAM  dos era
Convertir a minúscula
nuevo_discurso = nuevo_discurso.lower()
print(nuevo_discurso[0:100])
s e  la presidenta de la república  michelle bachelet  asiste al lanzamiento de la campaña “chile ne
Eliminar espacios en blanco innecesarios
nuevo_discurso = re.sub("\\s+", ' ', nuevo_discurso)
print(nuevo_discurso[0:100])
s e la presidenta de la república michelle bachelet asiste al lanzamiento de la campaña “chile neces
Eliminar tildes y líneas en blanco
from unicodedata import normalize


# -> NFD y eliminar diacríticos
nuevo_discurso = re.sub(
        r"([^n\u0300-\u036f]|n(?!\u0303(?![\u0300-\u036f])))[\u0300-\u036f]+", r"\1", 
        normalize( "NFD", nuevo_discurso), 0, re.I
    )

# -> NFC
nuevo_discurso = normalize( 'NFC', nuevo_discurso)
print(nuevo_discurso[0:200])
s e la presidenta de la republica michelle bachelet asiste al lanzamiento de la campaña “chile necesita mas medicos y especialistas incorporate al sistema publico de salud” amigas y amigos que duda ca

3. Tokenizar

Tokenizar significa dividir el texto en una unidad más pequeña, donde cada una de estas unidades va a ser un token. Por ejemplo, podemos tokenizar texto anterior en oraciones o en palabras:

  • Oraciones:

    • token 1 = Tokenizar significa dividir el texto en una unidad más pequeña

    • token 2 = donde cada una de estas unidades va a ser un token

  • Palabras:

    • token 1 = Tokenizar

    • token 2 = significa

    • token 3 = dividir

En nuestro ejemplo vamos a tokenizar en base a palabras.

from nltk.tokenize import word_tokenize
token = word_tokenize(nuevo_discurso)
print(token[0:10])
['s', 'e', 'la', 'presidenta', 'de', 'la', 'republica', 'michelle', 'bachelet', 'asiste']

4. Frecuencia de palabras

Uno de los primeros análisis que se puede realizar a partir de un texto, es la frecuencia por palabra, es decir cuántas veces se encuentra cada palabra dentro del texto.

from nltk.probability import FreqDist
fdist = FreqDist(token)
fdist
FreqDist({'de': 100, 'que': 62, 'y': 50, 'la': 48, 'en': 35, 'a': 31, 'el': 26, 'los': 25, 'las': 18, 'medicos': 17, ...})
# To find the frequency of top 10 words
fdist1 = fdist.most_common(10)
fdist1
[('de', 100),
 ('que', 62),
 ('y', 50),
 ('la', 48),
 ('en', 35),
 ('a', 31),
 ('el', 26),
 ('los', 25),
 ('las', 18),
 ('medicos', 17)]

5. Stopwords

En el ejemplo anterior vemos que las palabras más frecuentes son “de”, “que”, “y”, etc. Palabras que no aportan al análisis del texto, sino que son conectores, prepocisiones. Estas palabras son llamadas stopwords.

Para cada idioma existe un listado de stopwords y, dependiendo del contexto, se pueden ir modificando.

# nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words = list(stopwords.words('spanish'))
print(stop_words[0:10])
['de', 'la', 'que', 'el', 'en', 'y', 'a', 'los', 'del', 'se']
token_sw = [x for x in token if x not in stop_words]
print(token_sw[0:10])
['s', 'presidenta', 'republica', 'michelle', 'bachelet', 'asiste', 'lanzamiento', 'campaña', '\x93chile', 'necesita']
fdist = FreqDist(token_sw)
fdist1 = fdist.most_common(10)
fdist1
[('medicos', 17),
 ('salud', 13),
 ('mil', 12),
 ('mas', 11),
 ('atencion', 8),
 ('publico', 7),
 ('formacion', 7),
 ('cupos', 7),
 ('especialistas', 6),
 ('medico', 6)]

6. Wordclouds

Una de las visualizaciones clásicas del análisis de texto, es una representación visual de la frecuencia de palabas mediante una nube de palabras (wordclouds).

from wordcloud import WordCloud, STOPWORDS, ImageColorGenerator
import matplotlib.pyplot as plt

comment_words = ''
for i in range(len(token_sw)):
    comment_words += " ".join(token_sw)+" "
    
wordcloud = WordCloud().generate(comment_words)

plt.figure(figsize = (8, 8), facecolor = None)
plt.imshow(wordcloud)
plt.axis("off")
plt.tight_layout(pad = 0)
plt.show()
_images/Clase17_28_0.png
wordcloud = WordCloud(max_font_size=50, max_words=100, background_color="white").generate(comment_words)

plt.figure(figsize = (10, 10), facecolor = None)
plt.imshow(wordcloud, interpolation="bilinear")
# plt.imshow(wordcloud)
plt.axis("off")
plt.tight_layout(pad = 0)
plt.show()
_images/Clase17_29_0.png

7. Stemming

Stemming (derivado de) corresponde a una normalización de palabras llevandolas a la raíz. Por ejemplo las palabras médicos, medicas, médico, médica se pueden agrupar dentro de medic. Luego, en un procesamiento de información nos va ayudar a tener información más depurada.

Sin embargo, tenemos que tener precaución con estas reducciones de significado. Por ejemplo, un paquete podría reducir médico a medic y medicina a medicin, mientras que otro más agresivo podría llevar ambos conceptos a medic. Naturalmente, para efectos del análisis de la información los resultados pueden ser muy variados.

from nltk.stem import SnowballStemmer
stemmer = SnowballStemmer('spanish')

stemmer.stem('medico')
'medic'
token_st =[stemmer.stem(x) for x in token_sw]
token_st[0:10]
['s',
 'president',
 'republ',
 'michell',
 'bachelet',
 'asist',
 'lanzamient',
 'campañ',
 '\x93chil',
 'necesit']
comment_words = ''
for i in range(len(token_st)):
    comment_words += " ".join(token_st)+" "
    
wordcloud = WordCloud(max_font_size=50, max_words=100, background_color="white").generate(comment_words)

plt.figure(figsize = (8, 8), facecolor = None)
plt.imshow(wordcloud, interpolation="bilinear")
# plt.imshow(wordcloud)
plt.axis("off")
plt.tight_layout(pad = 0)
plt.show()
_images/Clase17_33_0.png

8. N-gramas

Podemos escribir “el computador está apagado”, pero sería incorrecto decir “apagado el está computador”. En este contexto, los N-gramas con estructuras de palabras que se suceden.

Por ejemplo:

  • Bi-grama: (el, computador), (computador, está)

  • Tri-grama: (el, computador, está), (computador, está, apagado)

Capturamos cierto significado de un conjunto de palabras.

from nltk import ngrams

sentence = 'this is a foo bar sentences and i want to ngramize it'

n = 3
n2grams = ngrams(nuevo_discurso.split(), n)
from nltk.probability import FreqDist
fdist = FreqDist(n2grams)
fdist
FreqDist({('sistema', 'publico', 'de'): 4, ('la', 'atencion', 'primaria'): 4, ('etapa', 'de', 'destinacion'): 4, ('de', 'destinacion', 'y'): 4, ('destinacion', 'y', 'formacion'): 4, ('\x93chile', 'necesita', 'mas'): 3, ('necesita', 'mas', 'medicos'): 3, ('mas', 'medicos', 'y'): 3, ('la', 'calidad', 'de'): 3, ('en', 'nuestro', 'pais'): 3, ...})
fdist1 = fdist.most_common(10)

x=[]
y=[]
for i in fdist1:
    x.append(str(i[0]))
    y.append(i[1])
print(x[0])
('sistema', 'publico', 'de')
import plotly.graph_objects as go

fig = go.Figure(go.Bar(
            x=y,
            y=x,
            orientation='h'))

fig.update_layout(width=700, height=500)
fig.show()

Clase 18: Introducción al uso de clases

En esta sesión veremos el uso de clases y programación orientada a objetos.

  1. Repaso

  2. Introducción a las clases

  3. Definir una clase

  4. Llamar funciones dentro de una clase

  5. Actualizar parámetros

  6. Ejercicio

1. Repaso

  1. Revisamos qué es Python, por qué es tan popular y cómo instalamos Anaconda y Jupyter.

  2. Aprendimos qué es una librería, cómo se instala y cómo utilizar diferentes módulos.

  3. Revisamos como crear variables y los diferentes types (tipos) que podemos usar.

  4. Vimos la diferentecia entre arrays y listas.

  5. Creamos loops para obtener iteraciones, cómo acceder a los elementos de una lista/array utilizando loops.

  6. Aprendimos el uso de condicionales: if/else.

  7. Creamos funciones (def), la flexibilidad de éstas y vimos qué es el input y output.

  8. Aprendimos qué es un diccionario, qué es una llave (key) y valor (value). Cómo nos sirve para ordenar nuestra información y cómo podemos vincularlo con loops y condicionales.

  9. Revisamos librerías de gŕaficos: Matplotlib y Plotly.

  10. Trabajamos con datos estructurados mediante la librería Pandas:

    • Importar datos

    • Limpiar información

    • Datos de texto, numéricos y fechas

    • Crear un DataFrame

    • Manipular el DataFrame

  11. Usando pandas aprendimos a manejar bases de datos relacionales: tipo SQL.

  12. Nos introdujimos al mundo del análisis de texto (text mining).

Librerías que hemos ocupados:

  • Numpy

  • Pandas

  • Matplotlib

  • Plotly

  • nltk

  • re

2. Introducción

La programación orientada a objectos es un enfoque de desarrollo de sistemas centrado en objetos y como interactúan. Por ejemplo, pensemos en los elementos de alrededor, un computador, celular, escritorio, cama, etc. ¿Cómo interactúan? Me conecto al computador, utilizo el celular, subo una foto a instagram, etc.

En la programación vamos a tener objetos y vamos a establecer mecanismos para que estos interactúen. Los códigos se van a enfocar en establecer objetos y clases.

En este contexto, un objeto va a ser una forma específica de organizar un código y que va a estar definida, de tal forma, que va a englobar los datos y el comportamiento. Esto significa que los datos y los procesos (funciones) van a pertenecer a uno o más objetos.

Otra forma de ver los procedemientos es el paradigma procedural (procedural paradigm), que en simple es dividir el problema en en pequeñas partes (funciones) y vamos combinando los procedimientos, donde se comparte data entre procesos o mediante variables globales. Por otro lado, la programación orientada a objetos divide el problema en pequeñas partes (objetos) y el código está construido alrededor de los objetos. “Los objetos buscan representar cosas que existen en el mundo real en un sistema de cómputo”. Los objetos no comparten datos.

Las clases (class) nos permiten ordenar datos y funciones. De forma incompleta e introductoria, podemos pensar las clases como una gran función de funciones, que nos va a permitir ordenar códigos que pueden ser grandes y con gran cantidad de sub-funciones.

Cuando creamos una nueva clase (class) vamos a crear un nuevo tipo (type) de objeto. Cada clase va a tener atributos particulares a ésta y métodos para modificarla.

Existen lenguajes que funcionan exclusivamente orientados a objetos, como Java, y otros como Python que son lenguajes multipropósito que permiten trabajar con mecanismos del tipo procedural u objetos.

Veamos un ejemplo

from IPython.display import Image
import numpy as np
import scipy.io as sio
Image("Clases.png")
_images/Clase18_4_0.png
Image("Clases2.png")
_images/Clase18_5_0.png

3. Definir una clase

class Empresa:
    #Dos cosas fundamentales: __init__() y "self"
     #init(): cada clase tiene la función __init__(). Se ejecuta cada vez se llama la clase. 
             # su función es asignar valores a los objetos. 
     #self: el parámetro self permite hacer referencia dentro de una instancia de la clase, 
             #sean parámetros o métodos. No necesariamente se tiene que llamar self. 
    def __init__(self, ingresos, costos, salarios, personas):
        self.ingresos = ingresos #variable única a cada instancia
        self.costos = costos
        self.salarios = salarios
        self.personas = personas


emp1 = Empresa("100", "50", "20", "10") #Probar quitando un argumento
print("Empresa 1:", "Ingresos, costos", (emp1.ingresos, emp1.costos))

emp2 = Empresa("1000", "200", "500", "30") #Probar quitando un argumento
print("Empresa 2:", "Ingresos, costos", (emp2.ingresos, emp2.costos))
Empresa 1: Ingresos, costos ('100', '50')
Empresa 2: Ingresos, costos ('1000', '200')

4. Llamar funciones dentro de una clase

# llamar funciones dentro de la clase
class Empresa:

    def __init__(self, ingresos, costos, salarios, personas):
        self.ingresos = float(ingresos) 
        self.costos = float(costos)
        self.salarios = float(salarios)
        self.personas = float(personas)

    def myfunc(self):
        ingresos = str(self.ingresos)
        costos = str(self.costos)
        print("La Empresa tiene ingresos y costos por " + ingresos + " y " + costos +  " respectivamente.")

    def utilidades(self): 
        ingresos = self.ingresos
        costos = self.costos
        return ingresos-costos
    
    def salario_pp(self): 
        salarios = self.salarios
        personas = self.personas
        return salarios/personas
    
    def utilidades_pp(self):
        utilidades = self.utilidades()
        personas = self.personas
        return utilidades/personas

        
ingresos = input('¿Cuál es el ingreso de la empresa?')        
costos = input('¿Cuáles son los costos de la empresa?')          
salarios = input('¿Cuál es el total de salarios?')
personas = input('¿Cuántas personas trabajan en la empresa?')

        
emp1 = Empresa(ingresos, costos, salarios, personas) #Probar quitando un argumento
---------------------------------------------------------------------------
StdinNotImplementedError                  Traceback (most recent call last)
<ipython-input-5-52e05aea364c> in <module>
     29 
     30 
---> 31 ingresos = input('¿Cuál es el ingreso de la empresa?')
     32 costos = input('¿Cuáles son los costos de la empresa?')
     33 salarios = input('¿Cuál es el total de salarios?')

~/miniconda3/lib/python3.9/site-packages/ipykernel/kernelbase.py in raw_input(self, prompt)
    843         """
    844         if not self._allow_stdin:
--> 845             raise StdinNotImplementedError(
    846                 "raw_input was called, but this frontend does not support input requests."
    847             )

StdinNotImplementedError: raw_input was called, but this frontend does not support input requests.
emp1.myfunc()
emp1.utilidades()
emp1.salario_pp()
emp1.utilidades_pp()
La Empresa tiene ingresos y costos por 100.0 y 10.0 respectivamente.
45.0

5. Actualizar parámetros

class Ej_param1:
 
    def __init__(self, α, β):
        self.α = α 
        self.β = β
        self.γ = self.α / self.β

    def myfunc(self):
        α, β, γ = self.α, self.β, self.γ
        return α * β**γ

        
α = 0.3
β = 0.7
par = Ej_param1(α, β)
resultado = par.myfunc() 
print(resultado)
0.2574747827902883
class Ej_param2:
 
    def __init__(self, α=0.5, β=0.7):
        self.α = α 
        self.β = β
        self.γ = self.α / self.β

        
    def myfunc(self):
        α, β, γ = self.α, self.β, self.γ
        return α * β**γ

par = Ej_param2(α = 0.3) #probar sin actualizar valores o actualizando valores 
resultado = par.myfunc() 
print(resultado)
0.2574747827902883
class Ej_param3:
 
    def __init__(self, **kwargs): 
        #unspecified number of arguments to a function
        #kwargs allows you to pass keyworded variable length of arguments to a function
        self.parameters() 
        self.update_parameters(kwargs)
        self.parameters2() 
        
    def parameters(self):
        #Parámetros básicos
        self.α = 0.5 
        self.β = 0.7
        
    def parameters2(self):
        #Parámetros compuestos
        self.γ = self.α / self.β
        
    def update_parameters(self, kwargs): 
        for key, value in kwargs.items():
            setattr(self, key, value)
            #syntax: setattr(object, name, value)
        
    def myfunc(self):
        α, β, γ = self.α, self.β, self.γ
        return α * β**γ

par = Ej_param3(α = 0.3) #probar sin actualizar valores o actualizando valores 
resultado = par.myfunc() 
print(resultado)
0.2574747827902883

6. Ejercicio 1

Supongamos que modelo de oferta y demanda:

\[S(q) = q^2\]
\[D(q) = (q-20)^2\]
  1. Definir una función para la oferta y otra para la de demanda.

  2. Graficar las curvas \(S(q)\) y \(D(q)\) para un set de \(q\in (0,16)\).

  3. Obtener el precio y cantidad de equilibrio.

  4. Graficar el equilibrio.

  5. Agregar todo lo anterior en una clase.

  1. Definir una función para la oferta y otra para la de demanda.

#1. Definir una función para la oferta y otra para la de demanda.
#Oferta
def S(q):
    return (q**2)

#Demanda
def D(q):
    return (q - 20)**2
  1. Graficar las curvas \(S(q)\) y \(D(q)\) para un set de \(q\in (0,16)\).

#2. Graficar las curvas $S(q)$ y $D(q)$ para un set de $q\in (0,16)$. 
#Librerías

%matplotlib inline 
    #Magic function: muestra los gráficos directo después de crearlos
import numpy as np
import matplotlib.pyplot as plt

#Crear vector de cantidad
q = np.linspace(0, 16, 1000)

#Graficar
plt.plot(q, S(q), label = "Oferta") #Gŕafico oferta
plt.plot(q, D(q), label = "Demanda") #Gráfico demanda
plt.title("Oferta y demanda") #Título
plt.legend() #Leyenda
plt.xlabel("Cantidad $q$") #Label eje x
plt.ylabel("Precio") #Label eje y
Text(0, 0.5, 'Precio')
_images/Clase18_19_1.png
  1. Obtener el precio y cantidad de equilibrio.

El equilibrio se encuentra donde la función de oferta y demanda se intersectan: $\(S(q) = D(q)\)$

#3. Obtener el precio y cantidad de equilibrio. 
#Librería: Symbolic Computation -> trabajar con problemas matemáticos simbólicamente
import sympy as sy

q = sy.Symbol('q')
print("q:", q)
eq = sy.Eq(S(q), D(q))
eq
q: q
\[\displaystyle q^{2} = \left(q - 20\right)^{2}\]
q_sol = sy.solve(eq)
print("q_sol:", q_sol)
q_sol: [10]
p_sol = S(q_sol[0])
print("Equilibrio (q,p):", (q_sol, p_sol))
Equilibrio (q,p): ([10], 100)
  1. Graficar el equilibrio.

#4. Graficar el equilibrio. 
plt.figure(figsize= (10, 8))#create figure and reset q to be numbers
q = np.linspace(0, 16, 1000)

plt.plot(q, S(q), label = "Oferta")#plot supply, demand, and equilibrium points
plt.plot(q, D(q), label = "Demanda")
plt.plot(q_sol[0], p_sol, 'o', markersize = 14)

plt.title("Oferta y demanda")#add titles and legend
plt.legend()
plt.xlabel("Cantidad $q$")
plt.ylabel("Precio")

ax = plt.axes()#add arrow with annotation
ax.annotate('Equilibrio (10, 100)', xy=(10,100), xytext=(10, 250), arrowprops=dict(facecolor='black'))
<ipython-input-15-06f2ed6b7c24>:14: MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance.  In a future version, a new instance will always be created and returned.  Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
  ax = plt.axes()#add arrow with annotation
Text(10, 250, 'Equilibrio (10, 100)')
_images/Clase18_25_2.png
  1. Agregar todo lo anterior en una clase.

class S_D: 
    def __init__(self, q, β=1):
        self.q = q
        self.β = β
    #Oferta
    def S(self):
        q = self.q
        β = self.β
        return (q**2)

    #Demanda
    def D(self):
        q = self.q
        return (q - 20)**2
    
    def sol(self): 
        β = self.β
        #Cantidad
        q = sy.Symbol('q')
        S = self.S()
        D = self.D()
        #Equilibrio : oferta=demanda
        eq = sy.Eq(S, D)
        
        #Solución
        q_sol = sy.solve(eq)
        p_sol = β*q_sol[0]**2
        
        return q_sol[0], p_sol
    
    def plot(self, Q): 
        #Llamar del self
#         q=self.q
        q_sol, p_sol = self.sol()
        self.q = Q
        
        plt.figure(figsize= (10, 8))
        
        plt.plot(Q, self.S(), label = "Oferta")#plot supply, demand, and equilibrium points
        plt.plot(Q, self.D(), label = "Demanda")
        plt.plot(q_sol, p_sol, 'o', markersize = 14)

        plt.title("Oferta y demanda")#add titles and legend
        plt.legend()
        plt.xlabel("Cantidad $q$")
        plt.ylabel("Precio")

        ax = plt.axes()#add arrow with annotation
        ax.annotate('Equilibrio' + str((q_sol, p_sol)), xy=(q_sol, p_sol), xytext=(q_sol, 250), arrowprops=dict(facecolor='black'))        
        plt.show()
    
Q = np.linspace(0.1, 16, 1000)
q = sy.Symbol('q')
sd = S_D(q, β=1)
sd.sol()
sd.plot(Q)
<ipython-input-31-2225e630ba8b>:48: MatplotlibDeprecationWarning: Adding an axes using the same arguments as a previous axes currently reuses the earlier instance.  In a future version, a new instance will always be created and returned.  Meanwhile, this warning can be suppressed, and the future behavior ensured, by passing a unique label to each axes instance.
  ax = plt.axes()#add arrow with annotation
_images/Clase18_28_1.png

7. Ejercicio 2

Usando el ejemplo de bases relacionales que vimos en clases, crear una clase que tenga como características las bases de nombre, edad y almuerzo.

  1. Crear una función que según un nombre muestre la hora promedio de almuerzo.

  2. Función que grafique el promedio de la hora de almuerzo por día.

  3. Crear función que devuelva el data frame con las variables id, nombre, edad, día, hora y almuerzo.

  4. Función que devuelva el DataFrame corregido, excluyendo los NaN.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
nombre = pd.read_excel("ejemplo_sql.xlsx", sheet_name='nombre', thousands=",")
edad = pd.read_excel("ejemplo_sql.xlsx", sheet_name='edad', thousands=",")
almuerzo = pd.read_excel("ejemplo_sql.xlsx", sheet_name='Almuerzo', thousands=",")

Clase 19: Scipy y computación numérica

En esta clase revisaremos el trabajo, la librería scipy.

Scipy

La librería scipy es una librería de computación científica que utiliza numpy. Nos va a permitir realizar optimización, procesos estadísticos, interpolación, procesamiento de imágenes, integración, entre otros.

Álgebra lineal

Para operaciones de álgebra lineal podemos ocupar el módulo linag.

#1. Determinante de una matriz
import numpy as np
from scipy import linalg
arr = np.array([[1, 2],
               [3, 4]])
linalg.det(arr)
-2.0
arr = np.array([[3, 2],
                [6, 4]])

linalg.det(arr)
0.0
#2. Inversa de una matriz
linalg.inv(arr)
# calcular la inversa de una matriz singular (su determinante es cero) generará un error
---------------------------------------------------------------------------
LinAlgError                               Traceback (most recent call last)
<ipython-input-3-317de499420c> in <module>
      1 #2. Inversa de una matriz
----> 2 linalg.inv(arr)
      3 # calcular la inversa de una matriz singular (su determinante es cero) generará un error

~/miniconda3/lib/python3.9/site-packages/scipy/linalg/basic.py in inv(a, overwrite_a, check_finite)
    966         inv_a, info = getri(lu, piv, lwork=lwork, overwrite_lu=1)
    967     if info > 0:
--> 968         raise LinAlgError("singular matrix")
    969     if info < 0:
    970         raise ValueError('illegal value in %d-th argument of internal '

LinAlgError: singular matrix
arr = np.array([[1, 2],
               [3, 4]])
linalg.inv(arr)
array([[-2. ,  1. ],
       [ 1.5, -0.5]])

Optimización

Podemos ocupar el módulo optimize para encontrar el mínimo (máximo) de una función (escalares o multidimensional).

from scipy import optimize
import matplotlib.pyplot as plt
def f(x):
    return x**2 + 10*np.sin(x)
x = np.arange(-10, 10, 0.1)
plt.plot(x, f(x)) 
plt.show() 
_images/Clase19_11_0.png

Mínimo global: corresponde al valor mínimo en el dominio de la función. Mínimo local: corresponde al valor mínimo en una región particular de la curva.

En el ejemplo tenemos un mínimo local y un mínimo global. Mediante un algoritmo de scipy podemos encontrar estos valores, por ejemplo podemos usar la función fmin_bfgs (método BFGS) para encontrar el mínimo.

#Usamos el módigo optmize, la función fmin_bfgs
#La sintaxis es fmin_bfgs(función a optimizar, valor inicial)
opt = optimize.fmin_bfgs(f, 0)
print(opt)
#El resultado nos entrega: 
#1) Valor de la función evaluada en el punto mínimo
#2) Número de iteraciones que se demoró en encontrar el mínimo
#3) Número de evaluaciones de la función
#4) Número de evaluaciones del gradiente
#5) Parámetro que minimiza la función
Optimization terminated successfully.
         Current function value: -7.945823
         Iterations: 5
         Function evaluations: 12
         Gradient evaluations: 6
[-1.30644012]

Por la naturaleza del método podemos quedar en el mínimo local si no partimos del “lado correcto”

optimize.fmin_bfgs(f, 3)
Optimization terminated successfully.
         Current function value: 8.315586
         Iterations: 6
         Function evaluations: 14
         Gradient evaluations: 7
array([3.83746709])

Cuando no sabemos el intervalo del mínimo global podemos ocupar un método costoso que utiliza la fuerza bruta, en que se evalúa la función en cada punto

grid = (-10, 10, 0.1)
xmin_global = optimize.brute(f, (grid,))
xmin_global
array([-1.30641113])

Podemos encontrar un mínimo local (dentro de un intervalo), usando fminbound

xmin_local = optimize.fminbound(f, 0, 10)    
print(xmin_local)
3.8374671194983834

Para encontrar las raíces de una función, podemos utilizar fsolve. La raíz corresponde al punto donde \(f(x)=0\).

root = optimize.fsolve(f, 1)  # our initial guess is 1
root
array([0.])

Si la función tiene más de una raíz, tenemos que cambiar el valor inicial para partir de un punto que nos lleve a la segunda (tercera…) solución

root2 = optimize.fsolve(f, -2.5)
root2
array([-2.47948183])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, f(x), 'b-', label="f(x)")
xmins = np.array([xmin_global[0], xmin_local])
ax.plot(xmins, f(xmins), 'go', label="Minima")
roots = np.array([root, root2])
ax.plot(roots, f(roots), 'kv', label="Roots")
ax.legend()
ax.set_xlabel('x')
ax.set_ylabel('f(x)')
Text(0, 0.5, 'f(x)')
_images/Clase19_24_1.png

Supongamos que \(f\) tiene un poco de ruido

xdata = np.linspace(-10, 10, num=20)
ydata = f(xdata) + np.random.randn(xdata.size)


plt.plot(x, f(x), 'b-', label="f(x)")
plt.plot(xdata, ydata, color="red")
[<matplotlib.lines.Line2D at 0x7f6a1004aa00>]
_images/Clase19_26_1.png

La forma funcional original es $\(f(x) = x^2 + 10*sin(x)\)$

Entonces podemos generalizar mediante

\[f(x,a,b) = a*x^2 + b*sin(x)\]

Mediante curve_fit() podemos encontrar los valores de a y b.

def f2(x,a ,b):
    return a*x**2 + b**np.sin(x)
#Valor inicial
guess = [2,2]

#curve_fit(f, xdata, ydata, valor inicial)
params, params_covariance = optimize.curve_fit(f2, xdata, ydata, guess)

#retorna: 
#1) Valores óptimos que minimizan f(xdata) - ydata
#2) Covarianza estimada de valores óptimos
params
array([ 0.95241285, 11.53081666])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, f(x), 'b-', label="f(x)")
ax.plot(x, f2(x, *params), 'r--', label="Curve fit result")

xmins = np.array([xmin_global[0], xmin_local])
ax.plot(xmins, f(xmins), 'go', label="Minima")
roots = np.array([root, root2])
ax.plot(roots, f(roots), 'kv', label="Roots")
ax.legend()
ax.set_xlabel('x')
ax.set_ylabel('f(x)')
Text(0, 0.5, 'f(x)')
_images/Clase19_30_1.png

Estadísticas y números aleatorios

Podemos ocupar el módulo scipy.stats tiene potentes herramientas estadísticas y probabilísticas.

Para la generación de procesos aleatorios podemos ocupar numpy.random

a = np.random.normal(size=10_000)
plt.plot(a)
[<matplotlib.lines.Line2D at 0x7f6a0ffa56a0>]
_images/Clase19_32_1.png
plt.hist(x=a, bins=50, alpha=0.7, rwidth=0.85);
_images/Clase19_33_0.png
mu, sigma = 2, 0.5
s = np.random.normal(mu, sigma, 10000)
plt.hist(x=s, bins=50, alpha=0.7, rwidth=0.85);
_images/Clase19_34_0.png
n, p = 10, .5 
s = np.random.binomial(n, p, 10_000)
plt.plot(s)
#Resultado de tirar una moneda 10 veces, testeado 10_000 veces
[<matplotlib.lines.Line2D at 0x7f6a0f8fa4c0>]
_images/Clase19_35_1.png
plt.hist(x=s, bins=10, alpha=0.7, rwidth=0.85);
_images/Clase19_36_0.png
sum(np.random.binomial(2, 0.1, 20000) == 0)/20000
#COrremos el modelo 20_000 veces, y vemos el resultado de la probabilidad que se generen valores igual a cero
0.81165

Probability density function for norm: $\(f(x) = \frac{exp(-x^2/2)}{\sqrt{2\pi}}\)$

#Probability density function
from scipy.stats import norm

#Obtener momentos
x = np.linspace(-3, 3, 1000)
pdf = norm.pdf(x)
plt.plot(x, pdf)
[<matplotlib.lines.Line2D at 0x7f6a0e9992b0>]
_images/Clase19_39_1.png
cdf = norm.cdf(x)
plt.plot(x, cdf)
[<matplotlib.lines.Line2D at 0x7f6a0e971d30>]
_images/Clase19_40_1.png
cdf = np.linspace(0, 1, 1000)
ppf = norm.ppf(cdf)
plt.plot(ppf, cdf)
[<matplotlib.lines.Line2D at 0x7f6a0e92e910>]
_images/Clase19_41_1.png

Interpolación

Cuando tenemos datos y no conocemos su forma funcional, podemos realizar una aproximación mediante una interpolación. Esto consiste en evaluar los puntos y obtener una aproximación de la función. En simple, nos permite mediante un set de valores discretos tener una función que nos permite tener un espacio continuo

Para esto podemos ocupar el módulo interpolate

#Creamos datos experimentales: ejm similar a una función seno
x = np.linspace(0, 1, 10)
noise = (np.random.random(10)*2 - 1) * 1e-1
valores = np.sin(2 * np.pi * x) + noise
plt.plot(x, valores);
_images/Clase19_44_0.png
from scipy.interpolate import interp1d

#Creamos valores nuevos
val_nuevos = np.linspace(0, 1, 50)
#Interpolación lineal
linear_interp = interp1d(x, valores)
linear_results = linear_interp(val_nuevos)
#Interpolación cúbica
cubic_interp = interp1d(x, valores, kind='cubic')
cubic_results = cubic_interp(val_nuevos)
plt.plot(x, valores, 'o', ms=6, label='Valores')
plt.plot(val_nuevos, linear_results, label='linear interp')
plt.plot(val_nuevos, cubic_results, label='cubic interp')
plt.legend()
plt.show()
_images/Clase19_46_0.png

Actividad

  1. Obtenga la inversa de una matriz a del tipo $\(a = \left[ \begin{array}{ccc} 3 & 2 & 0 \\ 1 & -1 & 0 \\ 0 & 5 & 1 \\ \end{array}\right]\)$

inv_a = linalg.inv(a)
  1. Resuelva el sistema \(a*x = b\), donde $\(b = \left[ \begin{array}{c} 2 \\ 4 \\ -1 \\ \end{array}\right]\)$

import numpy as np
a = np.array([[3, 2, 0], [1, -1, 0], [0, 5, 1]])

b = np.array([2, 4, -1])

from scipy import linalg

x = linalg.solve(a, b)
x
array([ 2., -2.,  9.])
  1. Si compara \(x = a^{-1}*b\) usando 1) con el resultado de 2), son iguales?

inv_a @ b
array([ 2., -2.,  9.])
4. 
4.0
b = 10;

def f(X): 
    return (X[0]-1)**2 + b*(X[1]-X[0]**2)**2
# Initialize figure 
import matplotlib.pyplot as plt
figRos = plt.figure(figsize=(12, 7))
axRos = figRos.gca(projection='3d')

# Evaluate function
X = np.arange(-2, 2, 0.15)
Y = np.arange(-1, 3, 0.15)
X, Y = np.meshgrid(X, Y)
Z = f([X,Y])

# Plot the surface
surf = axRos.plot_surface(X, Y, Z,
                       linewidth=0, antialiased=False)
axRos.set_zlim(0, 200)
figRos.colorbar(surf, shrink=0.5, aspect=10)
plt.show()
_images/Clase19_55_0.png
from scipy.optimize import least_squares
input = np.array([2, 2])
res = least_squares(f, input)
res
 active_mask: array([0., 0.])
        cost: 0.0021748096395843544
         fun: array([0.06595164])
        grad: array([-0.00102082,  0.01379367])
         jac: array([[-0.01547836,  0.20914825]])
     message: 'The maximum number of function evaluations is exceeded.'
        nfev: 200
        njev: 189
  optimality: 0.013793670721659863
      status: 0
     success: False
           x: array([1.25467248, 1.58466043])

Clase 20: Regresiones

En esta clase revisaremos los conceptos econométricos de Mínimos Cuadrados Ordinarios (OLS por sus siglas en inglés) y Modelo Autorregresivo (AR por sus siglas en inglés).

Para esto vamos a utilizar la librería statsmodels: https://www.statsmodels.org/stable/install.html

Mínimos Cuadrados Ordinarios (Ordinary Least Squares)

Buscamos estimar la Función de Regresión Poblacional (FRP), sin embargo esta no es observable directamente. Para esto vamos a utilizar una estimación que llamaremos la Función de Regresión Muestral (FRM):

  • FRP: \(Y_i = \beta_1 + \beta_2 X_i + \mu_i\)

  • FRM: \(Y_i = \hat{\beta}_1 + \hat{\beta}_2 X_i + \hat{\mu}_i = \hat{Y}_i - \hat{\mu}_i\), donde \(\hat{Y}_i\) es el valor estimado (media condicional) de \(Y_i\).

Entonces, en OLS vamos a minimizar el error \(\hat{\mu}_i = Y_i - \hat{Y_i}\) $\(\begin{align} \sum u_i^2 &= \sum (Y_i-\hat{Y}_i)^2 \\ &= \sum (Y_i - \hat{\beta}_1 - \hat{\beta}_2 X_i)^2 \end{align}\)$

from IPython.display import Image
Image("OLS1.png")
_images/Clase20_2_0.png
import pandas as pd
import numpy as np
df = pd.read_stata("/home/felix/Dropbox/Computational_Economics/Intro_python/Data_OLS_AR/Casen2020.dta");
/home/felix/miniconda3/lib/python3.9/site-packages/pandas/io/stata.py:1417: UnicodeWarning: 
One or more strings in the dta file could not be decoded using utf-8, and
so the fallback encoding of latin-1 is being used.  This can happen when a file
has been incorrectly encoded by Stata or some other software. You should verify
the string values returned are correct.
  warnings.warn(msg, UnicodeWarning)
detalle_ingreso = df.ytrabajocor.describe()
detalle_ingreso
count    7.350900e+04
mean     5.783447e+05
std      1.264676e+06
min      4.000000e+00
25%      2.000000e+05
50%      3.681670e+05
75%      6.500000e+05
max      2.250000e+08
Name: ytrabajocor, dtype: float64
df2 = df[(df.ytrabajocor>0) & (df.ytrabajocor<10_000_000) & (df.ytrabajocor.notna())]
df2.ytrabajocor.describe()
count    7.345000e+04
mean     5.617230e+05
std      6.973099e+05
min      4.000000e+00
25%      2.000000e+05
50%      3.674275e+05
75%      6.500000e+05
max      9.850000e+06
Name: ytrabajocor, dtype: float64
plt.plot(df2.ytrabajocor, 'g')
plt.xlabel("Observación")
plt.ylabel('Ingreso del trabajo')
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-5-b7adf0787577> in <module>
----> 1 plt.plot(df2.ytrabajocor, 'g')
      2 plt.xlabel("Observación")
      3 plt.ylabel('Ingreso del trabajo')

NameError: name 'plt' is not defined
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(8, 8))
plt.hist(df2.ytrabajocor, 50, density=True, facecolor='g', alpha=0.75)
plt.xlim([0, 5_000_000])
plt.xlabel('Ingreso del trabajo')
plt.ylabel('Densidad')
plt.show()
_images/Clase20_7_0.png
y_educ_bas = df2[["ytrabajocor", "educ"]][df2.educ=="Básica incompleta"].ytrabajocor
y_educ_uni = df2[["ytrabajocor", "educ"]][df2.educ=="Profesional completo"].ytrabajocor
fig = plt.figure(figsize=(8, 8))
plt.hist(y_educ_bas, 50, density=True, facecolor='g', alpha=0.75)
plt.hist(y_educ_uni, 50, density=True, facecolor='r', alpha=0.75)
plt.xlim([0, 5_000_000])
plt.xlabel('Ingreso del trabajo')
plt.ylabel('Densidad')
plt.show()
_images/Clase20_9_0.png

Datos para la regresión

y = df2.ytrabajocor
X = df2.esc
X = sm.add_constant(X)
X.head()
const esc
0 1.0 12.0
3 1.0 15.0
4 1.0 NaN
5 1.0 16.0
7 1.0 15.0
#Hay que limpiar los NaN en variable exógena
df3 = df[(df.ytrabajocor>0) & (df.ytrabajocor<10_000_000) & (df.ytrabajocor.notna()) & (df.esc.notna())]
y = df3.ytrabajocor
X = df3.esc
X = sm.add_constant(X)
X.head()
const esc
0 1.0 12.0
3 1.0 15.0
5 1.0 16.0
7 1.0 15.0
11 1.0 14.0
y = df3.ytrabajocor
X = df3.esc
X = sm.add_constant(X)
X.head()
import statsmodels.api as sm
model = sm.OLS(y, X)
results = model.fit()
print(results.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:            ytrabajocor   R-squared:                       0.183
Model:                            OLS   Adj. R-squared:                  0.183
Method:                 Least Squares   F-statistic:                 1.601e+04
Date:                Mon, 26 Jul 2021   Prob (F-statistic):               0.00
Time:                        22:21:34   Log-Likelihood:            -1.0554e+06
No. Observations:               71466   AIC:                         2.111e+06
Df Residuals:                   71464   BIC:                         2.111e+06
Df Model:                           1                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const      -3.694e+05   7751.498    -47.662      0.000   -3.85e+05   -3.54e+05
esc         7.572e+04    598.451    126.532      0.000    7.45e+04    7.69e+04
==============================================================================
Omnibus:                    64620.243   Durbin-Watson:                   1.630
Prob(Omnibus):                  0.000   Jarque-Bera (JB):          3238679.663
Skew:                           4.257   Prob(JB):                         0.00
Kurtosis:                      34.861   Cond. No.                         43.0
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
  • No. Observations: número de observaciones ocupadas en la regresión.

  • R-squared: El \(R^2\) es la proporción de la varianza en la variable dependiente que puede ser predicha por las variables independientes. En este caso un 18% de la varianza es explicada por las variables independientes.

  • Adj R-squared (\(R^2\) ajustado): el \(R^2\) se ajusta para penalizar que se agreguen regresores extraños.

  • F-statistic (F(Z,)): Es la razón entre el error cuadrático medio del modelo y el error cuadrático medio de los residuos. Nos sirve para determinar la importancia general del modelo.

  • Criterios de información (AIC y BIC): nos sirven para ver el trade-off entre complejidad del modelo y bondad de ajuste de este.

Image("OLS3.png")
_images/Clase20_20_0.png

En las filas están las variables con las que regresionamos: constante y educación. En las columnas tenemos:

  • El coeficiente de las variables independientes y la constante. Acá estamos estimando los \(\beta_i\): \(Y_i = \hat{\beta}_1 + \hat{\beta}_2 X_i + \hat{\mu}_i\)

  • std err: error estándar asociado a los coeficientes.

  • t: estadístico t usado para testear la significancia de los coeficientes.

  • \(P>|t|\): p-value de dos colas usado para testear hipótesis nula de que el coeficiente es cero (\(\alpha=0.05\)).

  • [0.025 - 0.975]: intervalo de confianza de coeficiente con \(\alpha=0.05\).

#df.loc[df['column name'] condition, 'new column name'] = 'value if condition is met'

df3.loc[df3.sexo=="Hombre", "sexo2"] = 0
df3.loc[df3.sexo=="Mujer", "sexo2"] = 1
/home/felix/miniconda3/lib/python3.9/site-packages/pandas/core/indexing.py:1599: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self.obj[key] = infer_fill_value(value)
/home/felix/miniconda3/lib/python3.9/site-packages/pandas/core/indexing.py:1720: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self._setitem_single_column(loc, value, pi)
y = df3.ytrabajocor
X = df3[["esc", "edad", "sexo2"]]
X = sm.add_constant(X)
X.head()
const esc edad sexo2 esc2
0 1.0 12.0 34 1.0 144.0
3 1.0 15.0 45 0.0 225.0
5 1.0 16.0 57 0.0 256.0
7 1.0 15.0 56 1.0 225.0
11 1.0 14.0 54 1.0 196.0
df3['exp'] = df3.edad-df3.esc
df3['exp2'] = df3.exp**2
df3['exp_sexo'] = df3.exp*df3.sexo2
y = np.log(df3.ytrabajocor)
X = df3[["esc", "exp", "exp2", "sexo2", "exp_sexo"]]
X = sm.add_constant(X)
X.head()
<ipython-input-18-79fecfcd28f3>:1: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df3['exp'] = df3.edad-df3.esc
<ipython-input-18-79fecfcd28f3>:2: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df3['exp2'] = df3.exp**2
<ipython-input-18-79fecfcd28f3>:3: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df3['exp_sexo'] = df3.exp*df3.sexo2
const esc exp exp2 sexo2 exp_sexo
0 1.0 12.0 22.0 484.0 1.0 22.0
3 1.0 15.0 30.0 900.0 0.0 0.0
5 1.0 16.0 41.0 1681.0 0.0 0.0
7 1.0 15.0 41.0 1681.0 1.0 41.0
11 1.0 14.0 40.0 1600.0 1.0 40.0
model = sm.OLS(y, X)
results = model.fit()
print(results.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:            ytrabajocor   R-squared:                       0.265
Model:                            OLS   Adj. R-squared:                  0.265
Method:                 Least Squares   F-statistic:                     5151.
Date:                Tue, 26 Oct 2021   Prob (F-statistic):               0.00
Time:                        11:08:12   Log-Likelihood:            -1.1887e+05
No. Observations:               71466   AIC:                         2.378e+05
Df Residuals:                   71460   BIC:                         2.378e+05
Df Model:                           5                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const          9.7314      0.030    326.486      0.000       9.673       9.790
esc            0.1522      0.002    100.943      0.000       0.149       0.155
exp            0.0820      0.001     71.234      0.000       0.080       0.084
exp2          -0.0011   1.56e-05    -68.238      0.000      -0.001      -0.001
sexo2         -0.1503      0.021     -7.152      0.000      -0.191      -0.109
exp_sexo      -0.0136      0.001    -23.036      0.000      -0.015      -0.012
==============================================================================
Omnibus:                    31679.342   Durbin-Watson:                   1.650
Prob(Omnibus):                  0.000   Jarque-Bera (JB):           197590.127
Skew:                          -2.052   Prob(JB):                         0.00
Kurtosis:                      10.037   Cond. No.                     1.12e+04
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.12e+04. This might indicate that there are
strong multicollinearity or other numerical problems.
y = np.log(df3.ytrabajocor)
X = df3[["esc", "edad", "sexo2"]]
X = sm.add_constant(X)
X.head()
const esc edad sexo2
0 1.0 12.0 34 1.0
3 1.0 15.0 45 0.0
5 1.0 16.0 57 0.0
7 1.0 15.0 56 1.0
11 1.0 14.0 54 1.0
model = sm.OLS(y, X)
results = model.fit()
print(results.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:            ytrabajocor   R-squared:                       0.212
Model:                            OLS   Adj. R-squared:                  0.212
Method:                 Least Squares   F-statistic:                     6423.
Date:                Mon, 26 Jul 2021   Prob (F-statistic):               0.00
Time:                        23:38:58   Log-Likelihood:            -1.2134e+05
No. Observations:               71466   AIC:                         2.427e+05
Df Residuals:                   71462   BIC:                         2.427e+05
Df Model:                           3                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const         10.5982      0.028    380.688      0.000      10.544      10.653
esc            0.1696      0.001    124.958      0.000       0.167       0.172
edad           0.0039      0.000     10.597      0.000       0.003       0.005
sexo2         -0.5787      0.010    -58.179      0.000      -0.598      -0.559
==============================================================================
Omnibus:                    32963.117   Durbin-Watson:                   1.642
Prob(Omnibus):                  0.000   Jarque-Bera (JB):           198868.520
Skew:                          -2.170   Prob(JB):                         0.00
Kurtosis:                       9.925   Cond. No.                         269.
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
print('Parámetros: ', results.params)
print('Error estandar: ', results.bse)
print('Valores predichos: ', results.predict())
Parámetros:  const    10.598153
esc       0.169602
edad      0.003898
sexo2    -0.578690
dtype: float64
Error estandar:  const    0.027839
esc      0.001357
edad     0.000368
sexo2    0.009947
dtype: float64
Valores predichos:  [12.18721169 13.31758263 13.5339568  ... 12.31583427 10.77744552
 11.22416493]

Comparar resultados predichos

from statsmodels.sandbox.regression.predstd import wls_prediction_std

prstd, iv_l, iv_u = wls_prediction_std(results)

fig, ax = plt.subplots(figsize=(8,6))

ax.plot(X.esc, y, 'o', label="data")
[<matplotlib.lines.Line2D at 0x7f4486a0d9d0>]
_images/Clase20_30_1.png

Para ver ejemplos de gráficos en modelos de regresión pueden utilizar https://www.statsmodels.org/stable/examples/notebooks/generated/regression_plots.html

Por ejemplo, una visualización de las regresiones parciales se puede obtener mediante plot_partregress_grid. Esto muestra la relación entre la respuesta y la variable explicativa después de eliminar el efecto de todas las otras variables exógenas.

#Para ver gráficos de las regresiones parciales (regresionar la variable dependiente c/r a una variable condicional en el resto de las variables exógenas)
fig = sm.graphics.plot_partregress_grid(results)
fig.tight_layout(pad=1.0)
_images/Clase20_32_0.png

Para ver información de la regresión con respecto a una variable en específico podemos ocupar plot_regress_exog. Acá tenemos

  • Fitted plot

  • Residuo

  • Regresión parcial

  • Component-Component plus Residual (CCPR) plot: nos sirve para ver la relación entre uan variable independiente particular y la variable de respuesta, dado que otras variables indpendientes también están presentes en el modelo: \(Res+\hat{\beta}_iX_i\) versus \(X_i\)

fig = sm.graphics.plot_regress_exog(results, "esc")
fig.tight_layout(pad=1.0)
_images/Clase20_34_0.png

Ejemplo caso con menos ruido: ejemplo de juguete

x = np.linspace(0, 10, 100)
y = x + 0.5 * np.random.normal(size=len(x))

plt.plot(x,y)
[<matplotlib.lines.Line2D at 0x7f44946253d0>]
_images/Clase20_36_1.png
X = np.column_stack((x, x**2))
X = sm.add_constant(X)
res = sm.OLS(y, X).fit()
print(res.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                      y   R-squared:                       0.974
Model:                            OLS   Adj. R-squared:                  0.973
Method:                 Least Squares   F-statistic:                     1807.
Date:                Mon, 26 Jul 2021   Prob (F-statistic):           1.73e-77
Time:                        23:31:42   Log-Likelihood:                -66.869
No. Observations:                 100   AIC:                             139.7
Df Residuals:                      97   BIC:                             147.6
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
const         -0.0904      0.141     -0.641      0.523      -0.370       0.189
x1             1.0865      0.065     16.670      0.000       0.957       1.216
x2            -0.0098      0.006     -1.557      0.123      -0.022       0.003
==============================================================================
Omnibus:                        0.781   Durbin-Watson:                   2.028
Prob(Omnibus):                  0.677   Jarque-Bera (JB):                0.817
Skew:                          -0.006   Prob(JB):                        0.665
Kurtosis:                       2.557   Cond. No.                         144.
==============================================================================

Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
# y_hat = res.params[0] + results.params[1] * x + results.params[1] * x**2
res.params[2]
-0.009817635455210468
prstd, iv_l, iv_u = wls_prediction_std(res)
#Retorno: 
#-standard error of prediction same length as rows of exog
#-lower und upper confidence bounds

fig, ax = plt.subplots(figsize=(8,6))

ax.plot(x, y, 'o', label="data")
ax.plot(x, res.fittedvalues, 'r--.', label="OLS")
ax.plot(x, iv_u, 'r--')
ax.plot(x, iv_l, 'r--')
ax.legend(loc='best');
_images/Clase20_40_0.png
fig, ax = plt.subplots(figsize=(8,6))

y_hat = res.params[0] + res.params[1] * x + res.params[2] * x**2

ax.plot(x, y, 'o', label="data")
ax.plot(x, res.fittedvalues, 'r--.', label="OLS")
ax.plot(x, y_hat, 'g', label="OLS")
[<matplotlib.lines.Line2D at 0x7f4494171280>]
_images/Clase20_41_1.png

Modelo Autorregresivo (AR)

Podemos estimar un modelo del tipo \(Y_t = \beta_0 + \beta_1 Y_{t-1} + \mu_t\)

En python podemos utilizar AutoReg: https://www.statsmodels.org/stable/examples/notebooks/generated/autoregressions.html

df = pd.read_excel("/home/felix/Dropbox/Computational_Economics/Intro_python/Data_OLS_AR/pib.xlsx")
df.dtypes
df.head()
Periodo PIB
0 1986-01-01 7899.666547
1 1986-04-01 8220.056395
2 1986-07-01 8355.276664
3 1986-10-01 8555.927503
4 1987-01-01 8626.993492
plt.plot(df.Periodo, df.PIB);
_images/Clase20_44_0.png
from pandas.plotting import lag_plot
lag_plot(df.PIB)
<AxesSubplot:xlabel='y(t)', ylabel='y(t + 1)'>
_images/Clase20_45_1.png
from statsmodels.tsa.ar_model import AutoReg, ar_select_order
from statsmodels.tsa.api import acf, pacf, graphics
mod = AutoReg(df.PIB, 1, old_names=False)
res = mod.fit()
print(res.summary())
                            AutoReg Model Results                             
==============================================================================
Dep. Variable:                    PIB   No. Observations:                  141
Model:                     AutoReg(1)   Log Likelihood               -1085.062
Method:               Conditional MLE   S.D. of innovations            562.000
Date:                Tue, 27 Jul 2021   AIC                             12.706
Time:                        08:19:50   BIC                             12.769
Sample:                             1   HQIC                            12.731
                                  141                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const        257.5324    123.602      2.084      0.037      15.278     499.787
PIB.L1         0.9985      0.005    203.603      0.000       0.989       1.008
                                    Roots                                    
=============================================================================
                  Real          Imaginary           Modulus         Frequency
-----------------------------------------------------------------------------
AR.1            1.0015           +0.0000j            1.0015            0.0000
-----------------------------------------------------------------------------

Podemos actualizar el modelo a \(Y_t = \beta_0 + \beta_1 Y_{t-1} + \beta_2 Y_{t-2} + \beta_3 Y_{t-3} + \beta_4 Y_{t-4} + \beta_5 Y_{t-5} + \mu_t\)

mod = AutoReg(df.PIB, 5, old_names=False)
res = mod.fit()
print(res.summary())
                            AutoReg Model Results                             
==============================================================================
Dep. Variable:                    PIB   No. Observations:                  141
Model:                     AutoReg(5)   Log Likelihood               -1047.475
Method:               Conditional MLE   S.D. of innovations            535.438
Date:                Tue, 27 Jul 2021   AIC                             12.669
Time:                        08:21:12   BIC                             12.819
Sample:                             5   HQIC                            12.730
                                  141                                         
==============================================================================
                 coef    std err          z      P>|z|      [0.025      0.975]
------------------------------------------------------------------------------
const        423.0718    133.111      3.178      0.001     162.179     683.965
PIB.L1         0.8942      0.083     10.802      0.000       0.732       1.056
PIB.L2         0.2591      0.123      2.105      0.035       0.018       0.500
PIB.L3        -0.3180      0.120     -2.658      0.008      -0.553      -0.083
PIB.L4        -0.5179      0.223     -2.323      0.020      -0.955      -0.081
PIB.L5         0.6816      0.195      3.488      0.000       0.299       1.065
                                    Roots                                    
=============================================================================
                  Real          Imaginary           Modulus         Frequency
-----------------------------------------------------------------------------
AR.1           -0.8762           -0.6971j            1.1197           -0.3930
AR.2           -0.8762           +0.6971j            1.1197            0.3930
AR.3            0.7558           -0.7735j            1.0814           -0.1268
AR.4            0.7558           +0.7735j            1.0814            0.1268
AR.5            1.0006           -0.0000j            1.0006           -0.0000
-----------------------------------------------------------------------------

Para un resumen de la visualización de los residuos estandarizados podemos ocupar plot_diagnostics

fig = plt.figure(figsize=(16,9))
fig = res.plot_diagnostics(fig=fig, lags=30)
#Standarized residual: 
    #-residuo: Valor observado - valor predicho
    #-standarized residual: residuo normalizado por el error estandar del modelo (RSE)
#Histogram plus: estima la distribución de los residuos estandarizados. Nos muestra la distribución normal (línea verde) como punto de comparación. 
#Normal Q-Q : muestra si el residuo está distribuido normalmente. Lo que se espera es que los puntos se encuentren sobre la línea roja o cercanos a ella. 
#Correlograma: nos muestra un gráfico de autocorrelaciones
_images/Clase20_51_0.png

Autocorrelaciones: https://www.statsmodels.org/stable/generated/statsmodels.graphics.tsaplots.plot_acf.html

sm.graphics.tsa.plot_acf(df.PIB, lags=40)
plt.show()
_images/Clase20_53_0.png

Actividad

  1. Estime la ecuación de mincer: \(Ingreso_i = \beta_0 + \beta_1 esc_i + \beta_2 exp_i + \beta_3 exp_i^2 + \beta_4 sexo_i + \mu_i\) donde :

    • ingreso: log(ingreso del trabajo)

    • esc: escolaridad

    • exp: experiencia = edad - escolaridad

    • exp2: expediencia al cuadrado

    • sexo: variable dicotómica si es hombre (0) o mujer (1).

  2. Estime un modelo AR del tipo \(\Delta y_t = \Delta y_{t-1}\) donde:

    • \(y_t\) es el PIB trimestral en el periodo t.

    • \(\Delta y_t\) es la variación del PIB trimestral en el periodo t.

    • \(\Delta y_{t-1}\) es la variación del PIB trimestral en el periodo t-1.